ContoursAndRelatedInfo.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. using OpenCvSharp;
  2. using PaintDotNet.Base.CommTool;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. namespace PaintDotNet.Base.Functionodel
  7. {
  8. /// <summary>
  9. /// 轮廓以及相关信息
  10. /// 最大卡规直径\最小卡规直径\平均卡规直径,还没计算
  11. /// </summary>
  12. public class ContoursAndRelatedInfo
  13. {
  14. /// <summary>
  15. /// 轮廓数组
  16. /// </summary>
  17. public OpenCvSharp.Point[] contours;
  18. /// <summary>
  19. /// 完整的轮廓结构
  20. /// </summary>
  21. public List<List<OpenCvSharp.Point>> wholeContours;
  22. /// <summary>
  23. /// 关系
  24. /// </summary>
  25. public HierarchyIndex hierarchyIndex;
  26. /// <summary>
  27. /// 标尺
  28. /// </summary>
  29. public double rule;
  30. /// <summary>
  31. /// 下标,用于绘制等
  32. /// </summary>
  33. public int index;
  34. /// <summary>
  35. /// 原图
  36. /// </summary>
  37. public Mat mat;
  38. /// <summary>
  39. /// 灰度图
  40. /// </summary>
  41. public Mat matGray;
  42. /// <summary>
  43. /// 二值的mat的灰度图,单通道
  44. /// </summary>
  45. public Mat binarizationMatGray;
  46. /// <summary>
  47. /// 原始图片的分割
  48. /// </summary>
  49. public Mat[] mats;
  50. /// <summary>
  51. /// mask
  52. /// </summary>
  53. public Mat hole;
  54. /// <summary>
  55. /// 卡规直径的32个值
  56. /// </summary>
  57. public List<int> vs;
  58. /// <summary>
  59. /// 卡规直径的32个角度的集合
  60. /// </summary>
  61. public List<OpenCvSharp.Point[]> plist;
  62. /// <summary>
  63. /// 最大卡规直径对应的点
  64. /// </summary>
  65. public OpenCvSharp.Point[] points_max;
  66. /// <summary>
  67. /// 最小卡规直径对应的点
  68. /// </summary>
  69. public OpenCvSharp.Point[] points_min;
  70. /// <summary>
  71. /// 面积
  72. /// </summary>
  73. public double area = -1;
  74. /// <summary>
  75. /// 面积比
  76. /// </summary>
  77. public double areaRatio = -1;
  78. /// <summary>
  79. /// 周长
  80. /// </summary>
  81. public double perimeter = -1;
  82. /// <summary>
  83. /// 长轴
  84. /// </summary>
  85. public double longAxis = -1;
  86. /// <summary>
  87. /// 短轴
  88. /// </summary>
  89. public double shortAxis = -1;
  90. /// <summary>
  91. /// 中心坐标X
  92. /// </summary>
  93. public int centerX = -1;
  94. /// <summary>
  95. /// 中心坐标Y
  96. /// </summary>
  97. public int centerY = -1;
  98. /// <summary>
  99. /// 倾斜角度
  100. /// </summary>
  101. public double inclinationAngle = -1;
  102. /// <summary>
  103. /// 物相外貌
  104. /// </summary>
  105. public double objectAppearance = -1;
  106. /// <summary>
  107. /// 高度
  108. /// </summary>
  109. public int height = -1;
  110. /// <summary>
  111. /// 宽度
  112. /// </summary>
  113. public int width = -1;
  114. /// <summary>
  115. /// 宽高比
  116. /// </summary>
  117. public double aspectRatio = -1;
  118. /// <summary>
  119. /// 红密度
  120. /// </summary>
  121. public double redDensity = -1;
  122. /// <summary>
  123. /// 绿密度
  124. /// </summary>
  125. public double greenDensity = -1;
  126. /// <summary>
  127. /// 蓝密度
  128. /// </summary>
  129. public double blueDensity = -1;
  130. /*/// <summary>
  131. /// 最大密度
  132. /// </summary>
  133. public double maxDensity = -1;
  134. /// <summary>
  135. /// 最小密度
  136. /// </summary>
  137. public double minDensity = -1;
  138. /// <summary>
  139. /// 平均密度
  140. /// </summary>
  141. public double avgDensity = -1;
  142. /// <summary>
  143. /// 密度和
  144. /// </summary>
  145. public double sumDensity = -1;*/
  146. /// <summary>
  147. /// 长径
  148. /// </summary>
  149. public double majorAxis = -1;
  150. /// <summary>
  151. /// 短径
  152. /// </summary>
  153. public double minorAxis = -1;
  154. /// <summary>
  155. /// 外接圆直径
  156. /// </summary>
  157. public float circumcircleDiameter = -1;
  158. /// <summary>
  159. /// 球化率
  160. /// </summary>
  161. public double nodularity = -1;
  162. /// <summary>
  163. /// 等积圆直径
  164. /// </summary>
  165. public double equalCircleDiameter = -1;
  166. /// <summary>
  167. /// 最大卡规直径
  168. /// </summary>
  169. public int maxCaliperDiameter = -1;
  170. /// <summary>
  171. /// 最小卡规直径
  172. /// </summary>
  173. public int minCaliperDiameter = -1;
  174. /// <summary>
  175. /// 平均卡规直径
  176. /// </summary>
  177. public int avgCaliperDiameter = -1;
  178. /// <summary>
  179. /// 最大灰度
  180. /// </summary>
  181. public double maxGray = -1;
  182. /// <summary>
  183. /// 最小灰度
  184. /// </summary>
  185. public double minGray = -1;
  186. /// <summary>
  187. /// 平均灰度
  188. /// </summary>
  189. public double avgGray = -1;
  190. /// <summary>
  191. /// 纤维长度
  192. /// </summary>
  193. public double fiberLength = -1;
  194. /// <summary>
  195. /// 填充面积
  196. /// </summary>
  197. public double fillArea = -1;
  198. /// <summary>
  199. /// 腰宽
  200. /// </summary>
  201. public double waistDepth = -1;
  202. /// <summary>
  203. /// 腰深
  204. /// </summary>
  205. public double waistDepthShort = -1;
  206. /// <summary>
  207. /// 获取面积
  208. /// 说明:轮廓的面积
  209. /// </summary>
  210. public double Area
  211. {
  212. get
  213. {
  214. if (this.area == -1)
  215. {
  216. if (contours != null && contours.Length > 0)
  217. {
  218. Rect rect;
  219. this.area = Cv2.FloodFill(this.binarizationMatGray, this.contours[0], new Scalar(0, 0, 0, 0), out rect, null, null, FloodFillFlags.Link8);
  220. //this.area = Cv2.ContourArea(contours);
  221. }
  222. }
  223. return this.area * rule * rule; // (this.area - 0/*this.contours.Length / 2.0*/)<0 ? 0 : (this.area - this.contours.Length / 2.0) * rule * rule; //this.contours.Length/2.0
  224. }
  225. }
  226. /// <summary>
  227. /// 获取面积比
  228. /// 说明:轮廓的面积/最小外接矩形的面积
  229. /// </summary>
  230. public double AreaRatio
  231. {
  232. get
  233. {
  234. if (this.areaRatio == -1)
  235. {
  236. RotatedRect rotatedRect = Cv2.MinAreaRect(contours);
  237. this.areaRatio = this.Area / (rotatedRect.Size.Width * rotatedRect.Size.Height * rule * rule);
  238. }
  239. return this.areaRatio>1 ? 1 : this.areaRatio;
  240. }
  241. }
  242. /// <summary>
  243. /// 获取周长
  244. /// 说明:轮廓的点的个数
  245. /// </summary>
  246. public double Perimeter
  247. {
  248. get
  249. {
  250. if (this.perimeter == -1)
  251. {
  252. this.perimeter = this.contours.Length;
  253. }
  254. return this.perimeter * rule;
  255. }
  256. }
  257. /// <summary>
  258. /// 长轴
  259. /// 说明:最小外接矩形的长边
  260. /// </summary>
  261. public double LongAxis
  262. {
  263. get
  264. {
  265. if (this.longAxis == -1)
  266. {
  267. RotatedRect rect = Cv2.MinAreaRect(this.contours);
  268. this.longAxis = rect.Size.Width > rect.Size.Height ? rect.Size.Width : rect.Size.Height;
  269. this.shortAxis = rect.Size.Width < rect.Size.Height ? rect.Size.Width : rect.Size.Height;
  270. }
  271. return this.longAxis * rule;
  272. }
  273. }
  274. /// <summary>
  275. /// 短轴
  276. /// 说明:最小外接矩形的短边
  277. /// </summary>
  278. public double ShortAxis
  279. {
  280. get
  281. {
  282. if (this.shortAxis == -1)
  283. {
  284. RotatedRect rect = Cv2.MinAreaRect(this.contours);
  285. this.longAxis = rect.Size.Width > rect.Size.Height ? rect.Size.Width : rect.Size.Height;
  286. this.shortAxis = rect.Size.Width < rect.Size.Height ? rect.Size.Width : rect.Size.Height;
  287. }
  288. return this.shortAxis * rule;
  289. }
  290. }
  291. /// <summary>
  292. /// 中心坐标X
  293. /// 说明:外接矩形的中心
  294. /// </summary>
  295. public int CenterX
  296. {
  297. get
  298. {
  299. if (centerX == -1)
  300. {
  301. Rect rect = Cv2.BoundingRect(this.contours);
  302. this.centerX = rect.X + rect.Width/2;
  303. this.centerY = rect.Y + rect.Height / 2;
  304. }
  305. return this.centerX;
  306. }
  307. }
  308. /// <summary>
  309. /// 中心坐标Y
  310. /// 说明:外接矩形的中心
  311. /// </summary>
  312. public int CenterY
  313. {
  314. get
  315. {
  316. if (centerX == -1)
  317. {
  318. Rect rect = Cv2.BoundingRect(this.contours);
  319. this.centerX = rect.X + rect.Width / 2;
  320. this.centerY = rect.Y + rect.Height / 2;
  321. }
  322. return this.centerY;
  323. }
  324. }
  325. /// <summary>
  326. /// 倾斜角度
  327. /// </summary>
  328. public double InclinationAngle
  329. {
  330. get
  331. {
  332. if (this.inclinationAngle == -1)
  333. {
  334. if (this.contours.Length >= 5)
  335. {
  336. RotatedRect rect = Cv2.FitEllipse(this.contours);
  337. this.objectAppearance = Math.Round(rect.Size.Width, 10) / rect.Size.Height;
  338. this.inclinationAngle = rect.Angle;
  339. }
  340. else
  341. {
  342. this.objectAppearance = 1.0;
  343. this.inclinationAngle = 0.0;
  344. }
  345. }
  346. return this.inclinationAngle;
  347. }
  348. }
  349. /// <summary>
  350. /// 物相外貌,拟合椭圆的长短轴比
  351. /// </summary>
  352. public double ObjectAppearance
  353. {
  354. get
  355. {
  356. if(this.objectAppearance == -1)
  357. {
  358. if (this.contours.Length >= 5)
  359. {
  360. RotatedRect rect = Cv2.FitEllipse(this.contours);
  361. this.objectAppearance = Math.Round(rect.Size.Width, 10) / rect.Size.Height;
  362. this.inclinationAngle = rect.Angle;
  363. }
  364. else
  365. {
  366. this.objectAppearance = 1.0;
  367. this.inclinationAngle = 0.0;
  368. }
  369. }
  370. return this.objectAppearance;
  371. }
  372. }
  373. /// <summary>
  374. /// 高度
  375. /// </summary>
  376. public double Height
  377. {
  378. get
  379. {
  380. if(this.height == -1)
  381. {
  382. Rect rect = Cv2.BoundingRect(this.contours);
  383. this.height = rect.Height;
  384. this.width = rect.Width;
  385. this.aspectRatio = this.width / (this.height * 1.0);
  386. }
  387. return this.height * rule;
  388. }
  389. }
  390. /// <summary>
  391. /// 宽度
  392. /// </summary>
  393. public double Width
  394. {
  395. get
  396. {
  397. if (this.width == -1)
  398. {
  399. Rect rect = Cv2.BoundingRect(this.contours);
  400. this.height = rect.Height;
  401. this.width = rect.Width;
  402. this.aspectRatio = this.width / (this.height * 1.0);
  403. }
  404. return this.width * rule;
  405. }
  406. }
  407. /// <summary>
  408. /// 宽高比
  409. /// </summary>
  410. public double AspectRatio
  411. {
  412. get
  413. {
  414. if(this.aspectRatio == -1)
  415. {
  416. Rect rect = Cv2.BoundingRect(this.contours);
  417. this.height = rect.Height;
  418. this.width = rect.Width;
  419. this.aspectRatio = this.width / (this.height * 1.0);
  420. }
  421. return this.aspectRatio;
  422. }
  423. }
  424. /// <summary>
  425. /// 红密度
  426. /// </summary>
  427. public double RedDensity
  428. {
  429. get
  430. {
  431. if(this.redDensity == -1)
  432. {
  433. if(this.mat != null)
  434. {
  435. if(mat.Type()== MatType.CV_8UC3 || mat.Type() == MatType.CV_8UC4)
  436. {
  437. hole.SetTo(Scalar.All(-1));
  438. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  439. double temp;
  440. Cv2.MinMaxIdx(mats[0], out temp, out this.blueDensity, new int[1], new int[1], hole);
  441. Cv2.MinMaxIdx(mats[1], out temp, out this.greenDensity, new int[1], new int[1], hole);
  442. Cv2.MinMaxIdx(mats[2], out temp, out this.redDensity, new int[1], new int[1], hole);
  443. }
  444. }
  445. }
  446. return this.redDensity;
  447. }
  448. }
  449. /// <summary>
  450. /// 绿密度
  451. /// </summary>
  452. public double GreenDensity
  453. {
  454. get
  455. {
  456. if (this.greenDensity == -1)
  457. {
  458. if (this.mat != null)
  459. {
  460. if (mat.Type() == MatType.CV_8UC3 || mat.Type() == MatType.CV_8UC4)
  461. {
  462. hole.SetTo(Scalar.All(-1));
  463. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  464. double temp;
  465. Cv2.MinMaxIdx(mats[0], out temp, out this.blueDensity, new int[1], new int[1], hole);
  466. Cv2.MinMaxIdx(mats[1], out temp, out this.greenDensity, new int[1], new int[1], hole);
  467. Cv2.MinMaxIdx(mats[2], out temp, out this.redDensity, new int[1], new int[1], hole);
  468. /*Mat temp1 = DrawRulerHelper.CopyMatFromContours(this.mat, this.wholeContours);
  469. double temp;
  470. Mat[] mats = temp1.Split();
  471. Cv2.MinMaxIdx(mats[0], out temp, out this.blueDensity);
  472. Cv2.MinMaxIdx(mats[1], out temp, out this.greenDensity);
  473. Cv2.MinMaxIdx(mats[2], out temp, out this.redDensity);*/
  474. }
  475. }
  476. }
  477. return this.greenDensity;
  478. }
  479. }
  480. /// <summary>
  481. /// 蓝密度
  482. /// </summary>
  483. public double BlueDensity
  484. {
  485. get
  486. {
  487. if (this.blueDensity == -1)
  488. {
  489. if (this.mat != null)
  490. {
  491. if (mat.Type() == MatType.CV_8UC3 || mat.Type() == MatType.CV_8UC4)
  492. {
  493. hole.SetTo(Scalar.All(-1));
  494. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  495. double temp;
  496. Cv2.MinMaxIdx(mats[0], out temp, out this.blueDensity, new int[1], new int[1], hole);
  497. Cv2.MinMaxIdx(mats[1], out temp, out this.greenDensity, new int[1], new int[1], hole);
  498. Cv2.MinMaxIdx(mats[2], out temp, out this.redDensity, new int[1], new int[1], hole);
  499. /*Mat temp1 = DrawRulerHelper.CopyMatFromContours(this.mat, this.wholeContours);
  500. double temp;
  501. Mat[] mats = temp1.Split();
  502. Cv2.MinMaxIdx(mats[0], out temp, out this.blueDensity);
  503. Cv2.MinMaxIdx(mats[1], out temp, out this.greenDensity);
  504. Cv2.MinMaxIdx(mats[2], out temp, out this.redDensity);*/
  505. }
  506. }
  507. }
  508. return this.blueDensity;
  509. }
  510. }
  511. /*/// <summary>
  512. /// 最大密度
  513. /// 说明:按AxioVision的说明
  514. /// Highest gray value of a region
  515. /// The gray values of a region are analyzed and the maximum value displayed.This is the brightest point in a region.If this parameter is measured for regions inside an RGB color image, the RGB color image is first converted to a gray level image in accordance with the formula (R+G+B) / 3 and the corresponding maximum value is then displayed.If you want the maximum values to be determined separately for one or all color channels, you need to use the Densitometric maximum blue, Densitometric maximum green and Densitometric maximum red parameters. This parameter can also be used to measure the maximum gray value of regions in multichannel fluorescence images.As a multichannel fluorescence image consists of several gray level images, no special parameter is needed.
  516. /// Unit: "gray levels" or "gray"
  517. /// Value range: 0...65535 (depending on the pixel or color depth of the image).
  518. /// </summary>
  519. public double MaxDensity
  520. {
  521. get
  522. {
  523. return this.maxDensity;
  524. }
  525. }
  526. /// <summary>
  527. /// 最小密度
  528. /// </summary>
  529. public double MinDensity
  530. {
  531. get
  532. {
  533. return this.minDensity;
  534. }
  535. }
  536. /// <summary>
  537. /// 平均密度
  538. /// Average of all gray values of a region
  539. /// The gray values of a region are analyzed and the average of all gray values displayed.If this parameter is measured for regions inside an RGB color image, the RGB color image is first converted to a gray level image in accordance with the formula (R+G+B) / 3 and the corresponding average is then displayed.If you want the averages to be determined separately for one or all color channels, you need to use the Densitometric mean blue, Densitometric mean green and Densitometric mean red parameters. This parameter can also be used to measure the average gray value of regions in multichannel fluorescence images.As a multichannel fluorescence image consists of several gray level images, no special parameter is needed.
  540. /// Unit: "gray levels" or "gray"
  541. /// Value range: 0...65535 (depending on the pixel or color depth of the image).
  542. /// </summary>
  543. public double AvgDensity
  544. {
  545. get
  546. {
  547. return avgDensity;
  548. }
  549. }
  550. /// <summary>
  551. /// 密度和
  552. /// Sum of gray values of a region
  553. /// All gray values of a region are added together.If this parameter is measured for regions inside an RGB color image, the RGB color image is first converted to a gray level image in accordance with the formula (R+G+B) / 3 and the sum is then calculated. If you want the sum to be determined separately for one or all color channels, you need to use the Densitometric sum blue, Densitometric sum green and Densitometric sum red parameters. This parameter can also be used to measure the sum of regions in multichannel fluorescence images. As a multichannel fluorescence image consists of several gray level images, no special parameter is needed.
  554. /// Unit: "gray levels" or "gray"
  555. /// Name in data tables: DensSum1
  556. /// </summary>
  557. public double SumDensity
  558. {
  559. get
  560. {
  561. return this.sumDensity;
  562. }
  563. }*/
  564. /// <summary>
  565. /// 长径(暂时理解为最小外接矩形的长边的一半)
  566. /// </summary>
  567. public double MajorAxis
  568. {
  569. get
  570. {
  571. if(this.majorAxis == -1)
  572. {
  573. this.majorAxis = this.LongAxis / 2;
  574. }
  575. return this.majorAxis;
  576. }
  577. }
  578. /// <summary>
  579. /// 短径(暂时理解为最小外接矩形的短边的一半)
  580. /// </summary>
  581. public double MinorAxis
  582. {
  583. get
  584. {
  585. if(this.minorAxis==-1)
  586. {
  587. this.minorAxis = this.ShortAxis / 2;
  588. }
  589. return this.minorAxis;
  590. }
  591. }
  592. /// <summary>
  593. /// 外接圆直径 = 最小外接圆的直径
  594. /// </summary>
  595. public float CircumcircleDiameter
  596. {
  597. get
  598. {
  599. if(this.circumcircleDiameter == -1)
  600. {
  601. Point2f point;
  602. float radius;
  603. if (this.contours.Length > 500)
  604. {
  605. Cv2.MinEnclosingCircle(Cv2.ConvexHull(this.contours, true), out point, out radius);
  606. }
  607. else
  608. {
  609. Cv2.MinEnclosingCircle(this.contours, out point, out radius);
  610. }
  611. this.circumcircleDiameter = radius * 2;
  612. this.nodularity = this.Area / (Math.PI * radius * radius * rule * rule);
  613. }
  614. return (float)(this.circumcircleDiameter * rule);
  615. }
  616. }
  617. /// <summary>
  618. /// 球化率 = 面积 / 最小外接圆的面积
  619. /// </summary>
  620. public double Nodularity
  621. {
  622. get
  623. {
  624. if (this.nodularity == -1)
  625. {
  626. Point2f point;
  627. float radius;
  628. if (this.contours.Length>500)
  629. {
  630. Cv2.MinEnclosingCircle(Cv2.ConvexHull(this.contours, true), out point, out radius);
  631. }
  632. else
  633. {
  634. Cv2.MinEnclosingCircle(this.contours, out point, out radius);
  635. }
  636. this.circumcircleDiameter = radius * 2;
  637. this.nodularity = this.Area / (Math.PI * radius * radius * rule * rule);
  638. }
  639. return this.nodularity>1 ? 1 : this.nodularity;
  640. }
  641. }
  642. /// <summary>
  643. /// 等积圆直径
  644. /// </summary>
  645. public double EqualCircleDiameter
  646. {
  647. get
  648. {
  649. if (this.equalCircleDiameter==-1)
  650. {
  651. this.equalCircleDiameter = Math.Sqrt(this.Area / Math.PI) * 2;
  652. }
  653. return this.equalCircleDiameter;
  654. }
  655. }
  656. /// <summary>
  657. /// 最大卡规直径
  658. /// </summary>
  659. public double MaxCaliperDiameter
  660. {
  661. get
  662. {
  663. if (this.maxCaliperDiameter == -1)
  664. {
  665. vs = BasicCalculationHelper.GetCaliperDiameter(this.contours, this.hierarchyIndex, out plist);
  666. this.maxCaliperDiameter = vs.Max();
  667. this.avgCaliperDiameter = (int)vs.Average();
  668. this.minCaliperDiameter = vs.Min();
  669. }
  670. return this.maxCaliperDiameter * rule;
  671. }
  672. }
  673. /// <summary>
  674. /// 最小卡规直径
  675. /// </summary>;
  676. public double MinCaliperDiameter
  677. {
  678. get
  679. {
  680. if (this.minCaliperDiameter == -1)
  681. {
  682. vs = BasicCalculationHelper.GetCaliperDiameter(this.contours, this.hierarchyIndex, out plist);
  683. this.maxCaliperDiameter = vs.Max();
  684. this.avgCaliperDiameter = (int)vs.Average();
  685. this.minCaliperDiameter = vs.Min();
  686. }
  687. return this.minCaliperDiameter * rule;
  688. }
  689. }
  690. /// <summary>
  691. /// 平均卡规直径
  692. /// </summary>
  693. public double AvgCaliperDiameter
  694. {
  695. get
  696. {
  697. if (this.avgCaliperDiameter == -1)
  698. {
  699. vs = BasicCalculationHelper.GetCaliperDiameter(this.contours, this.hierarchyIndex, out plist);
  700. this.maxCaliperDiameter = vs.Max();
  701. this.avgCaliperDiameter = (int)vs.Average();
  702. this.minCaliperDiameter = vs.Min();
  703. }
  704. return this.avgCaliperDiameter * rule;
  705. }
  706. }
  707. /// <summary>
  708. /// 最大灰度
  709. /// </summary>
  710. public double MaxGray
  711. {
  712. get
  713. {
  714. if(this.maxGray == -1)
  715. {
  716. hole.SetTo(Scalar.All(-1));
  717. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  718. this.matGray.MinMaxIdx(out this.minGray, out this.maxGray, new int[1], new int[1], hole);
  719. }
  720. return this.maxGray;
  721. }
  722. }
  723. /// <summary>
  724. /// 最小灰度
  725. /// </summary>
  726. public double MinGray
  727. {
  728. get
  729. {
  730. if (this.minGray == -1)
  731. {
  732. hole.SetTo(Scalar.All(-1));
  733. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  734. this.matGray.MinMaxIdx(out this.minGray, out this.maxGray, new int[1], new int[1], hole);
  735. }
  736. return this.minGray;
  737. }
  738. }
  739. public List<int> Vs
  740. {
  741. get
  742. {
  743. return this.vs;
  744. }
  745. set
  746. {
  747. this.vs = value;
  748. }
  749. }
  750. /// <summary>
  751. /// 平均灰度
  752. /// </summary>
  753. public double AvgGray
  754. {
  755. get
  756. {
  757. if (this.avgGray == -1)
  758. {
  759. hole.SetTo(Scalar.All(-1));
  760. Cv2.FillPoly(hole, wholeContours, Scalar.All(255));
  761. Mat mean = new Mat();
  762. Cv2.MeanStdDev(this.matGray, mean, new Mat(), hole);
  763. mean.ConvertTo(mean, MatType.CV_8UC1);
  764. this.avgGray = mean.At<byte>(0, 0);
  765. mean.Dispose();
  766. }
  767. return this.avgGray;
  768. }
  769. }
  770. /// <summary>
  771. /// 纤维长度
  772. /// 说明:公式参考AxioVision,(周长 + (周长的平方-16*面积)的平方根)/4
  773. /// </summary>
  774. public double FiberLength
  775. {
  776. get
  777. {
  778. return rule * (this.Perimeter + Math.Sqrt(this.Perimeter * this.Perimeter + 16 * this.Area)) / 4;
  779. }
  780. }
  781. /// <summary>
  782. /// 填充面积
  783. /// </summary>
  784. public double FillArea
  785. {
  786. get
  787. {
  788. if(this.fillArea == -1)
  789. {
  790. hole.SetTo(Scalar.All(-1));
  791. List<List<Point>> list = new List<List<Point>>();
  792. list.Add(this.contours.ToList());
  793. Cv2.FillPoly(this.hole, list, new Scalar(255, 255, 255, 0), LineTypes.Link8);
  794. Rect rect;
  795. int area_t = Cv2.FloodFill(this.hole, this.contours[0], new Scalar(0, 0, 0, 0), out rect, null, null, FloodFillFlags.Link8);
  796. this.fillArea = area_t * rule * rule - this.Area;
  797. }
  798. return this.fillArea;
  799. }
  800. }
  801. /// <summary>
  802. /// 腰深
  803. /// </summary>
  804. public double WaistDepth
  805. {
  806. get
  807. {
  808. if (this.waistDepth == -1)
  809. {
  810. //需要根据index 获取旋转的角度,再把点旋转回来
  811. if (this.MinCaliperDiameter > 0)
  812. {
  813. points_max = new Point[2];
  814. points_min = new Point[2];
  815. int max = vs.Max();
  816. int min = vs.Min();
  817. int index_max = vs.FindIndex(a => a == max);
  818. int index_min = vs.FindIndex(a => a == min);
  819. //计算中心点
  820. OpenCvSharp.Moments m = OpenCvSharp.Cv2.Moments(contours);
  821. double cx = m.M10 / m.M00;
  822. double cy = m.M01 / m.M00;
  823. try
  824. {
  825. //最大卡规直径的点
  826. points_max[0] = BasicCalculationHelper.GetPointAffinedPos(plist[index_max][0], new OpenCvSharp.Point(cx, cy), -360 / 64 * index_max);
  827. points_max[1] = BasicCalculationHelper.GetPointAffinedPos(plist[index_max][1], new OpenCvSharp.Point(cx, cy), -360 / 64 * index_max);
  828. //最小卡规直径的点,但是没有明显意义
  829. points_min[0] = BasicCalculationHelper.GetPointAffinedPos(plist[index_min][0], new OpenCvSharp.Point(cx, cy), -360 / 64 * index_min);
  830. points_min[1] = BasicCalculationHelper.GetPointAffinedPos(plist[index_min][1], new OpenCvSharp.Point(cx, cy), -360 / 64 * index_min);
  831. //寻找最大卡规直径的中垂线再轮廓上的点
  832. //1、把两个点都旋转九十度,得到两个新的点
  833. double cx1 = (points_max[0].X + points_max[1].X) / 2;
  834. double cy1 = (points_max[0].Y + points_max[1].Y) / 2;
  835. System.Drawing.Point a1 = BasicCalculationHelper.GetAnglePoint(new System.Drawing.Point(points_max[0].X, points_max[0].Y), new System.Drawing.Point((int)cx1, (int)cy1), 90);
  836. System.Drawing.Point b1 = BasicCalculationHelper.GetAnglePoint(new System.Drawing.Point(points_max[1].X, points_max[1].Y), new System.Drawing.Point((int)cx1, (int)cy1), 90);
  837. List<System.Drawing.Point> ps = BasicCalculationHelper.CalcExtensionCord(a1, b1);
  838. a1 = ps[0];
  839. b1 = ps[1];
  840. //Cv2.Line(this.mat, points_max[0], points_max[1], new Scalar(0, 0, 255), 3);
  841. //Cv2.Line(this.mat, new Point(a1.X, a1.Y), new Point(b1.X, b1.Y), new Scalar(0, 0, 255), 3);
  842. List<System.Drawing.Point> intersections = new List<System.Drawing.Point>();
  843. LineIterator it = new LineIterator(this.mat, new Point(a1.X, a1.Y), new Point(b1.X, b1.Y), PixelConnectivity.Connectivity8);
  844. foreach (var lip in it)
  845. {
  846. OpenCvSharp.Point p = lip.Pos;
  847. if (Math.Abs(Cv2.PointPolygonTest(contours, p, true)) < 1)
  848. {
  849. intersections.Add(new System.Drawing.Point(p.X, p.Y));
  850. //Cv2.Circle(this.mat, p, 1, new Scalar(255, 0, 0), 3);
  851. }
  852. }
  853. this.waistDepth = BasicCalculationHelper.GetLeftAndRightOrTopAndBottomPoint(intersections) * rule;
  854. }
  855. catch(Exception)
  856. {
  857. this.waistDepth = 0;
  858. }
  859. }
  860. else
  861. {
  862. this.waistDepth = 0;
  863. }
  864. }
  865. return this.waistDepth;
  866. }
  867. }
  868. /// <summary>
  869. /// 腰宽
  870. /// </summary>
  871. public double WaistDepthShort
  872. {
  873. get
  874. {
  875. if (this.waistDepthShort == -1)
  876. {
  877. this.waistDepthShort = this.MinCaliperDiameter - this.WaistDepth;
  878. }
  879. return this.waistDepthShort<0 ? 0 : this.waistDepthShort;
  880. }
  881. }
  882. }
  883. }