SliceHelper.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. using System;
  2. using System.IO;
  3. using System.Text;
  4. using System.Linq;
  5. using System.Drawing;
  6. using System.IO.Compression;
  7. using System.Threading.Tasks;
  8. using System.Collections.Generic;
  9. using System.Runtime.InteropServices;
  10. using AI.Common;
  11. using AI.Common.Log;
  12. using AI.Common.Tools;
  13. namespace AI.Reconstruction
  14. {
  15. /// <summary>
  16. /// 模型尺寸
  17. /// </summary>
  18. public struct ModelSize
  19. {
  20. public int ModelLengthX;
  21. public int ModelLengthY;
  22. public int ModelLengthZ;
  23. public int OneFacePixelsNum;
  24. }
  25. /// <summary>
  26. /// 模型切片类型
  27. /// </summary>
  28. public enum ModelSliceType
  29. {
  30. XEqual = 0,
  31. YEqual,
  32. ZEqual,
  33. Normal
  34. }
  35. /// <summary>
  36. /// 模型切面
  37. /// </summary>
  38. public struct ModelSlice
  39. {
  40. //切片像素点数据
  41. public byte[] SliceImageData;
  42. //图像的宽
  43. public int SliceWidth;
  44. //图像的高
  45. public int SliceHeight;
  46. }
  47. /// <summary>
  48. /// cefsharp的输入数据
  49. /// </summary>
  50. public struct CefInputDatas
  51. {
  52. // Point list
  53. public IList<Point3DF> PointsList;
  54. // Clip image name
  55. public string ClipImageName;
  56. }
  57. /// <summary>
  58. /// 共面的三个点
  59. /// </summary>
  60. public struct CoplanarPoints
  61. {
  62. private Point3DF _p1;
  63. private Point3DF _p2;
  64. private Point3DF _p3;
  65. /// <summary>
  66. /// 第一个点
  67. /// </summary>
  68. public Point3DF Point1 { get => _p1; set => _p1 = value; }
  69. /// <summary>
  70. /// 第二个点
  71. /// </summary>
  72. public Point3DF Point2 { get => _p2; set => _p2 = value; }
  73. /// <summary>
  74. /// 第三个点
  75. /// </summary>
  76. public Point3DF Point3 { get => _p3; set => _p3 = value; }
  77. /// <summary>
  78. /// 构造函数
  79. /// </summary>
  80. /// <param name="point1"></param>
  81. /// <param name="point2"></param>
  82. /// <param name="point3"></param>
  83. public CoplanarPoints(Point3DF point1, Point3DF point2, Point3DF point3)
  84. {
  85. _p1 = point1;
  86. _p2 = point2;
  87. _p3 = point3;
  88. }
  89. /// <summary>
  90. /// 空的CoplanarPoints
  91. /// </summary>
  92. public static readonly CoplanarPoints Empty = new CoplanarPoints(Point3DF.Empty, Point3DF.Empty, Point3DF.Empty);
  93. /// <summary>
  94. /// 判断是否相等
  95. /// </summary>
  96. /// <param name="one"></param>
  97. /// <param name="other"></param>
  98. /// <returns></returns>
  99. public static bool Equals(CoplanarPoints one, CoplanarPoints other)
  100. {
  101. return one.Point1 == other.Point1 && one.Point2 == other.Point2 && one.Point3 == other.Point3;
  102. }
  103. /// <summary>
  104. /// 判断是否相等
  105. /// </summary>
  106. /// <param name="left"></param>
  107. /// <param name="right"></param>
  108. /// <returns></returns>
  109. public static bool operator ==(CoplanarPoints left, CoplanarPoints right)
  110. {
  111. return Equals(left, right);
  112. }
  113. /// <summary>
  114. /// 判断是否相等
  115. /// </summary>
  116. /// <param name="left"></param>
  117. /// <param name="right"></param>
  118. /// <returns></returns>
  119. public static bool operator !=(CoplanarPoints left, CoplanarPoints right)
  120. {
  121. return !Equals(left, right);
  122. }
  123. }
  124. /// <summary>
  125. /// 获得切片图像的类
  126. /// </summary>
  127. public class SliceHelper
  128. {
  129. //模型边界x最大值
  130. private double _xEdge;
  131. //模型边界y最大值
  132. private double _yEdge;
  133. //模型边界z最大值
  134. private double _zEdge;
  135. //模型的尺寸
  136. private ModelSize _modelSize;
  137. //读取到的模型数据
  138. private byte[][] _modelData;
  139. //一般切片
  140. private ModelSlice _generalModelSlice;
  141. //缩放后切片
  142. private ModelSlice _resizedModelSlice;
  143. //是否已经设置模型源文件
  144. private bool _isSourceBeSet;
  145. //确定一个平面的三个不共线的点
  146. private One3DPlaneThreeBasicPoints _inputThreeBasic3DPoints;
  147. //切片的端点list,世界点坐标
  148. private List<Point3DF> _endPointList3D;
  149. //切片的端点list,图像点坐标
  150. private List<Point2D> _endPointList2I;
  151. //切片的端点list,图像端点极坐标点坐标
  152. private readonly List<Point2DF> _endPointList2DPolar;
  153. //三维坐标变换参数
  154. private CoordinateTransformation3D _coordinateTransformation3D;
  155. //切片的宽,变成2的n次方
  156. private int _sliceWidth2N;
  157. //切片的高,变成2的n次方
  158. private int _sliceHeight2N;
  159. //切片图像的最大长度
  160. private int _sliceMaxLength;
  161. //扫描深度
  162. private float _scanDepth;
  163. //切片图像坐标系的基础3D点
  164. private One2DPlaneThreeBasic3DPoints _planeBasic3DPoints;
  165. //切片图像坐标系的基础2D点
  166. private One2DPlaneThreeBasic2DPoints _planeBasic2DPoints;
  167. //多线程线程个数
  168. private readonly ParallelOptions _parallelOption = new ParallelOptions { MaxDegreeOfParallelism = 2 * Environment.ProcessorCount };
  169. //private static SliceHelper _instance;
  170. //public static SliceHelper Instance
  171. //{
  172. // get => _instance ?? (_instance = new SliceHelper());
  173. //}
  174. #region dllImport
  175. [DllImport(@"PlaqueProcessingCpp.dll", CallingConvention = CallingConvention.Cdecl)]
  176. [return: MarshalAs(UnmanagedType.I1)]
  177. private static extern bool Get2DMeasureImage(AI.Reconstruction.AIReconstructor.StructImageInfo srcImageInfo, int dataSize, ref AI.Reconstruction.AIReconstructor.StructImageInfo dstImageInfo);
  178. #endregion
  179. public SliceHelper()
  180. {
  181. _generalModelSlice = new ModelSlice();
  182. _resizedModelSlice = new ModelSlice();
  183. _inputThreeBasic3DPoints = new One3DPlaneThreeBasicPoints();
  184. _endPointList3D = new List<Point3DF>();
  185. _endPointList2I = new List<Point2D>();
  186. _endPointList2DPolar = new List<Point2DF>();
  187. }
  188. public ModelSize GetModelSize()
  189. {
  190. return _modelSize;
  191. }
  192. public byte[][] GetModelData()
  193. {
  194. return _modelData;
  195. }
  196. /// <summary>
  197. /// 设置模型来源
  198. /// </summary>
  199. /// <param name="mdlFilePath">mdl file</param>
  200. /// <param name="width">model's width distance</param>
  201. /// <param name="height">model's height distance</param>
  202. /// <param name="depth">model's depth distance</param>
  203. /// <param name="scanDepth">scan depth</param>
  204. /// <returns>Whether set model source successfully</returns>
  205. public bool SetModelSource(string mdlFilePath, int width, int height, int depth, float scanDepth)
  206. {
  207. try
  208. {
  209. if (depth < 5)
  210. {
  211. LogHelper.ErrorLog("SliceHelper,Do SetModelSource,depth<5.");
  212. return false;
  213. }
  214. if (_isSourceBeSet)
  215. {
  216. Release();
  217. }
  218. _isSourceBeSet = false;
  219. _modelSize = new ModelSize
  220. {
  221. ModelLengthX = width,
  222. ModelLengthY = height,
  223. ModelLengthZ = depth,
  224. OneFacePixelsNum = width * height
  225. };
  226. _modelData = new byte[_modelSize.ModelLengthZ][];
  227. for (var i = 0; i < _modelSize.ModelLengthZ; ++i)
  228. {
  229. _modelData[i] = new byte[_modelSize.OneFacePixelsNum];
  230. }
  231. if (!ParseMdl(mdlFilePath, depth))
  232. {
  233. return false;
  234. }
  235. _sliceMaxLength = GetMaxImageLength();
  236. _generalModelSlice.SliceImageData = new byte[_sliceMaxLength];
  237. _resizedModelSlice.SliceImageData = new byte[_sliceMaxLength];
  238. //计算模型边界
  239. _xEdge = _modelSize.ModelLengthX - 1 + MathTools3D.Precision;
  240. _yEdge = _modelSize.ModelLengthY - 1 + MathTools3D.Precision;
  241. _zEdge = _modelSize.ModelLengthZ - 1 + MathTools3D.Precision;
  242. _inputThreeBasic3DPoints = new One3DPlaneThreeBasicPoints
  243. {
  244. PointA = new Point3DF(0, 0, 0),
  245. PointB = new Point3DF(0, 0, 0),
  246. PointC = new Point3DF(0, 0, 0)
  247. };
  248. _scanDepth = scanDepth;
  249. _isSourceBeSet = true;
  250. return true;
  251. }
  252. catch (Exception e)
  253. {
  254. LogHelper.ErrorLog("SliceHelper, SetModelSource error," + e.Message + "," + e.StackTrace);
  255. return false;
  256. }
  257. }
  258. /// <summary>
  259. /// 解析模型
  260. /// </summary>
  261. private bool ParseMdl(string mdlFilePath, int depth)
  262. {
  263. using (var stream = new FileStream(mdlFilePath, FileMode.Open))
  264. {
  265. var fileStreamReader = new AIStreamReader(stream);
  266. var version = fileStreamReader.ReadString();
  267. if (version == "V1")
  268. {
  269. var compressedBytes = fileStreamReader.ReadBytes();
  270. using (var ms = new MemoryStream(compressedBytes))
  271. {
  272. using (GZipStream zipStream = new GZipStream(ms, CompressionMode.Decompress))
  273. {
  274. var zipStreamReader = new AIStreamReader(zipStream);
  275. return FillImageArray(zipStreamReader, depth);
  276. }
  277. }
  278. }
  279. else
  280. {
  281. //fileStreamReader.Reset();
  282. stream.Position = 0;
  283. return FillImageArray(fileStreamReader, depth);
  284. }
  285. }
  286. }
  287. /// <summary>
  288. /// 数组填充6个表面图像
  289. /// </summary>
  290. private bool FillImageArray(AIStreamReader reader, int depth)
  291. {
  292. var imgCount = reader.ReadInt();
  293. if (imgCount != depth)
  294. {
  295. LogHelper.ErrorLog("mdl file's image count not equal with model depth.");
  296. return false;
  297. }
  298. for (var i = 0; i < imgCount; ++i)
  299. {
  300. var readBytes = reader.ReadBytes();
  301. byte[] decodedDataBuffer = new byte[_modelSize.OneFacePixelsNum];
  302. if (!AIReconstructor.ImDataDecode(readBytes, readBytes.Length, decodedDataBuffer, decodedDataBuffer.Length))
  303. {
  304. int errorMaxLen = 256;
  305. StringBuilder errorMsg = new StringBuilder(errorMaxLen);
  306. AIReconstructor.EnumCppCoreErrorCode errorCode = AIReconstructor.EnumCppCoreErrorCode.None;
  307. AIReconstructor.GetErrorCodeAndMsg(ref errorCode, errorMsg, errorMaxLen);
  308. throw new Exception("Failed at decoding model image datas, error code: "
  309. + errorCode.ToString() + " , details: " + errorMsg.ToString() + " .");
  310. }
  311. Array.Copy(decodedDataBuffer, 0, _modelData[i], 0, _modelSize.OneFacePixelsNum);
  312. }
  313. return true;
  314. }
  315. /// <summary>
  316. /// 释放资源
  317. /// </summary>
  318. /// <returns></returns>
  319. public void Release()
  320. {
  321. _generalModelSlice.SliceImageData = null;
  322. _resizedModelSlice.SliceImageData = null;
  323. _modelData = null;
  324. GC.Collect();
  325. }
  326. /// <summary>
  327. /// 获取模型最大的切面尺寸
  328. /// </summary>
  329. /// <returns></returns>
  330. private int GetMaxImageLength()
  331. {
  332. var lenList = new List<int> { _modelSize.ModelLengthX, _modelSize.ModelLengthY, _modelSize.ModelLengthZ };
  333. var maxHypotenuse1 = Math.Sqrt(lenList[0] * lenList[0] + lenList[1] * lenList[1]);
  334. var maxHypotenuse2 = Math.Sqrt(lenList[1] * lenList[1] + lenList[2] * lenList[2]);
  335. var maxHypotenuse3 = Math.Sqrt(lenList[0] * lenList[0] + lenList[2] * lenList[2]);
  336. var maxLength = Math.Max(maxHypotenuse1, maxHypotenuse2);
  337. maxLength = Math.Max(maxLength, maxHypotenuse3);
  338. return (int)Math.Round(maxLength * maxLength);
  339. }
  340. /// <summary>
  341. /// 计算三维切片图像
  342. /// </summary>
  343. /// <param name="inputData">Input data include points >= 3 and clip category</param>
  344. /// <param name="savePath">Save path</param>
  345. /// <returns>True if calculate successfully.</returns>
  346. public bool Calculate3DSliceImage(CefInputDatas inputData, string savePath)
  347. {
  348. if (string.IsNullOrWhiteSpace(savePath))
  349. {
  350. LogHelper.ErrorLog("SliceHelper, slice image's save path is empty");
  351. return false;
  352. }
  353. if (!GetModelSlice(inputData))
  354. {
  355. LogHelper.ErrorLog("SliceHelper, Calculate3DSliceImage GetModelSlice failed.");
  356. return false;
  357. }
  358. return SaveImage(savePath);
  359. }
  360. /// <summary>
  361. /// 计算模型切片
  362. /// </summary>
  363. /// <param name="inputData"></param>
  364. /// <returns></returns>
  365. public bool GetModelSlice(CefInputDatas inputData)
  366. {
  367. try
  368. {
  369. //判断输入数据是否合法,当前是否可以获取切片
  370. if (!CheckBeforeGetModelSlice(inputData))
  371. {
  372. return false;
  373. }
  374. //获得基础点的三个点
  375. if (!GetBasePoint(inputData))
  376. {
  377. LogHelper.ErrorLog("SliceHelper,Input point is parallel.");
  378. return false;
  379. }
  380. //获得切面类型
  381. var sliceType = MathTools3D.GetModelSliceType(_inputThreeBasic3DPoints);
  382. //计算切面与模型的交点
  383. _endPointList3D = MathTools3D.GetIntersectionOfPlaneAndModel(_inputThreeBasic3DPoints, _modelSize, sliceType);
  384. //计算旋转矩阵
  385. _coordinateTransformation3D = GetCoordinateTransformation();
  386. //获得三维点对应的二维点
  387. _endPointList2I = MathTools3D.GetRotatedImageEndPoints(_endPointList3D, _planeBasic3DPoints, sliceType);
  388. //顺时针排序端点
  389. MathTools3D.SortEndPointClockwise(_planeBasic2DPoints, _endPointList3D, _endPointList2I);
  390. //获得切片图像大小
  391. GetSlicePanelSize(sliceType);
  392. //设置切面图像像素点的值
  393. return SetModelSliceData(sliceType);
  394. }
  395. catch (Exception e)
  396. {
  397. LogHelper.ErrorLog(" SliceHelper, GetModelSlice error." + e.Message + "," + e.StackTrace);
  398. return false;
  399. }
  400. }
  401. /// <summary>
  402. /// 判断输入数据是否合法,当前是否可以获取切片
  403. /// </summary>
  404. /// <param name="inputData"></param>
  405. /// <returns></returns>
  406. private bool CheckBeforeGetModelSlice(CefInputDatas inputData)
  407. {
  408. //模型是否已设置
  409. if (!_isSourceBeSet)
  410. {
  411. LogHelper.ErrorLog("SliceHelper,Model source not be set");
  412. return false;
  413. }
  414. //输入点个数是否小于3
  415. if (inputData.PointsList.Count < 3)
  416. {
  417. LogHelper.ErrorLog("SliceHelper,input point count is less than 3");
  418. return false;
  419. }
  420. //输入点是否在模型内
  421. foreach (var point in inputData.PointsList)
  422. {
  423. //if (ModelEdgeIsInModel(point)) continue;
  424. if (point.X > MathTools3D.NegPrecision && point.X < _xEdge &&
  425. point.Y > MathTools3D.NegPrecision && point.Y < _yEdge &&
  426. point.Z > MathTools3D.NegPrecision && point.Z < _zEdge) continue;
  427. LogHelper.ErrorLog(
  428. $"SliceHelper,Slice input point error: out of model x:{point.X}, y:{point.Y}, z:{point.Z}");
  429. return false;
  430. }
  431. return true;
  432. }
  433. /// <summary>
  434. /// 计算旋转矩阵
  435. /// </summary>
  436. /// <param name="endPointList3D"></param>
  437. /// <returns></returns>
  438. private CoordinateTransformation3D GetCoordinateTransformation()
  439. {
  440. //旋转后的图像坐标系,基于的三个世界点
  441. _planeBasic3DPoints = MathTools3D.GetOne2DPlaneBasicPoints(_endPointList3D);
  442. //计算_plane2DBasic3DPoints对应的二维点
  443. _planeBasic2DPoints = MathTools3D.ChangePlaneBasic3DPointTo2D(_planeBasic3DPoints);
  444. //根据坐标系基础点的三维坐标和二维坐标计算旋转矩阵
  445. return MathTools3D.GetCoordinateTransformationParameter(_planeBasic3DPoints, _planeBasic2DPoints);
  446. }
  447. /// <summary>
  448. /// 获得切片最小尺寸
  449. /// </summary>
  450. private void GetSlicePanelSize(ModelSliceType sliceType)
  451. {
  452. //获得切片像素值
  453. switch (sliceType)
  454. {
  455. case ModelSliceType.XEqual:
  456. _generalModelSlice.SliceWidth = _modelSize.ModelLengthY;
  457. _generalModelSlice.SliceHeight = _modelSize.ModelLengthZ;
  458. break;
  459. case ModelSliceType.YEqual:
  460. _generalModelSlice.SliceWidth = _modelSize.ModelLengthX;
  461. _generalModelSlice.SliceHeight = _modelSize.ModelLengthZ;
  462. break;
  463. case ModelSliceType.ZEqual:
  464. _generalModelSlice.SliceWidth = _modelSize.ModelLengthX;
  465. _generalModelSlice.SliceHeight = _modelSize.ModelLengthY;
  466. break;
  467. default:
  468. _generalModelSlice.SliceWidth = _endPointList2I.Max(m => m.X);
  469. _generalModelSlice.SliceHeight = _endPointList2I.Max(m => m.Y);
  470. break;
  471. }
  472. var imageLength = _generalModelSlice.SliceWidth * _generalModelSlice.SliceHeight;
  473. if (_sliceMaxLength < imageLength)
  474. {
  475. _generalModelSlice.SliceImageData = new byte[imageLength];
  476. _resizedModelSlice.SliceImageData = new byte[imageLength];
  477. //_modelMeasureSlice = new ModelMeasureSlice(imageLength);
  478. _sliceMaxLength = imageLength;
  479. }
  480. }
  481. /// <summary>
  482. /// 设置切面图像像素点的值
  483. /// </summary>
  484. /// <param name="sliceType"></param>
  485. /// <returns></returns>
  486. private bool SetModelSliceData(ModelSliceType sliceType)
  487. {
  488. try
  489. {
  490. //获得切片像素值
  491. switch (sliceType)
  492. {
  493. case ModelSliceType.XEqual:
  494. return GetModeSliceDataX((int)Math.Round(_inputThreeBasic3DPoints.PointA.X));
  495. case ModelSliceType.YEqual:
  496. return GetModeSliceDataY((int)Math.Round(_inputThreeBasic3DPoints.PointA.Y));
  497. case ModelSliceType.ZEqual:
  498. return GetModeSliceDataZ((int)Math.Round(_inputThreeBasic3DPoints.PointA.Z));
  499. case ModelSliceType.Normal:
  500. return GetModeSliceDataNormal();
  501. default:
  502. return false;
  503. }
  504. }
  505. catch (Exception e)
  506. {
  507. LogHelper.ErrorLog("SliceHelper, SetModelSliceData error," + e.Message + "," + e.StackTrace);
  508. return false;
  509. }
  510. }
  511. /// <summary>
  512. /// 获得基础的三个点
  513. /// </summary>
  514. /// <param name="inputData"></param>
  515. /// <returns></returns>
  516. private bool GetBasePoint(CefInputDatas inputData)
  517. {
  518. var count = inputData.PointsList.Count;
  519. for (var i = 0; i < count; ++i)
  520. {
  521. for (var j = 0; j < count; ++j)
  522. {
  523. for (var z = 0; z < count; ++z)
  524. {
  525. if (i == j || j == z || i == z) continue;
  526. //计算切面的基础点
  527. _inputThreeBasic3DPoints.PointA = MathTools3D.ChangePrecision(inputData.PointsList[i]);
  528. _inputThreeBasic3DPoints.PointB = MathTools3D.ChangePrecision(inputData.PointsList[j]);
  529. _inputThreeBasic3DPoints.PointC = MathTools3D.ChangePrecision(inputData.PointsList[z]);
  530. if (!MathTools3D.IsParallel(_inputThreeBasic3DPoints.PointA, _inputThreeBasic3DPoints.PointB, _inputThreeBasic3DPoints.PointC))
  531. {
  532. return true;
  533. }
  534. }
  535. }
  536. }
  537. return false;
  538. }
  539. /// <summary>
  540. /// 计算最终图像的大小
  541. /// </summary>
  542. /// <returns></returns>
  543. private Size GetDstImageSize()
  544. {
  545. int dstWidth;
  546. int dstHeight;
  547. //宽
  548. if (_generalModelSlice.SliceWidth < 512)
  549. {
  550. dstWidth = _generalModelSlice.SliceWidth >= 256 ? 256 : 128;
  551. }
  552. else
  553. {
  554. dstWidth = 512;
  555. }
  556. //高
  557. if (_generalModelSlice.SliceHeight < 512)
  558. {
  559. dstHeight = _generalModelSlice.SliceHeight >= 256 ? 256 : 128;
  560. }
  561. else
  562. {
  563. dstHeight = 512;
  564. }
  565. return new Size(dstWidth, dstHeight);
  566. }
  567. /// <summary>
  568. /// 计算切片端点的极坐标
  569. /// </summary>
  570. /// <param name="ratioWidth"></param>
  571. /// <param name="ratioHeight"></param>
  572. private void ChangeToPolarCoordinates(int ratioWidth, int ratioHeight)
  573. {
  574. try
  575. {
  576. _endPointList2DPolar.Clear();
  577. var dstWidth = _sliceWidth2N / ratioWidth;
  578. var dstHeight = _sliceHeight2N / ratioHeight;
  579. foreach (var point in _endPointList2I)
  580. {
  581. var a = (int)((float)point.X / ratioWidth + 0.5);
  582. var b = dstHeight - (int)((float)point.Y / ratioHeight + 0.5);
  583. var a1 = Math.Abs(a / (float)dstWidth);
  584. var b1 = Math.Abs(b / (float)dstHeight);
  585. a1 = Math.Min(Math.Max(a1, 0), 1);
  586. b1 = Math.Min(Math.Max(b1, 0), 1);
  587. _endPointList2DPolar.Add(new Point2DF(a1, b1));
  588. }
  589. }
  590. catch (Exception e)
  591. {
  592. LogHelper.ErrorLog("SliceHelper, ChangeToPolarCoordinates error:", e.Message);
  593. }
  594. }
  595. /// <summary>
  596. /// x坐标相等时获得切片图像数据的函数
  597. /// </summary>
  598. /// <param name="point1"></param>
  599. /// <returns></returns>
  600. private bool GetModeSliceDataX(double xPos)
  601. {
  602. var x = (int)Math.Round(xPos);
  603. x = Math.Min(_modelSize.ModelLengthX - 1, x);
  604. x = Math.Max(0, x);
  605. Parallel.For(0, _generalModelSlice.SliceHeight, _parallelOption, h =>
  606. {
  607. var dstIndex = h * _generalModelSlice.SliceWidth;
  608. var z = _modelSize.ModelLengthZ - 1 - h;
  609. var srcIndex = _modelSize.ModelLengthX * (_modelSize.ModelLengthY - 1) + x;
  610. for (var w = 0; w < _generalModelSlice.SliceWidth; ++w)
  611. {
  612. _generalModelSlice.SliceImageData[dstIndex] = _modelData[z][srcIndex];
  613. ++dstIndex;
  614. srcIndex -= _modelSize.ModelLengthX;
  615. }
  616. });
  617. return true;
  618. }
  619. /// <summary>
  620. /// y坐标相等时获得切片图像数据的函数
  621. /// </summary>
  622. /// <param name="yPos"></param>
  623. /// <returns></returns>
  624. private bool GetModeSliceDataY(double yPos)
  625. {
  626. var y = (int)Math.Round(yPos);
  627. y = Math.Min(_modelSize.ModelLengthY - 1, y);
  628. y = Math.Max(0, y);
  629. var z = _modelSize.ModelLengthZ - 1;
  630. var srcIndex = (y + 1) * _modelSize.ModelLengthX - 1;
  631. Parallel.For(0, _generalModelSlice.SliceHeight, _parallelOption, h =>
  632. {
  633. var z1 = z - h;
  634. var dstIndex = h * _generalModelSlice.SliceWidth;
  635. for (var w = 0; w < _generalModelSlice.SliceWidth; ++w)
  636. {
  637. _generalModelSlice.SliceImageData[dstIndex] = _modelData[z1][srcIndex - w];
  638. ++dstIndex;
  639. }
  640. });
  641. return true;
  642. }
  643. /// <summary>
  644. /// z坐标相等时获得切片图像数据的函数
  645. /// </summary>
  646. /// <param name="zPos"></param>
  647. /// <returns></returns>
  648. private bool GetModeSliceDataZ(double zPos)
  649. {
  650. var z = (int)Math.Round(zPos);
  651. z = Math.Min(_modelSize.ModelLengthZ - 1, z);
  652. z = Math.Max(0, z);
  653. var dstIndex = 0;
  654. var srcIndex = _modelSize.ModelLengthX * _modelSize.ModelLengthY - 1;
  655. var count = _generalModelSlice.SliceHeight * _generalModelSlice.SliceWidth;
  656. for (var i = 0; i < count; ++i)
  657. {
  658. _generalModelSlice.SliceImageData[dstIndex] = _modelData[z][srcIndex];
  659. ++dstIndex;
  660. --srcIndex;
  661. }
  662. return true;
  663. }
  664. /// <summary>
  665. /// XYZ坐标都不相等时获得切片图像数据的函数
  666. /// </summary>
  667. /// <param name="point1"></param>
  668. /// <param name="point2"></param>
  669. /// <param name="point3"></param>
  670. /// <returns></returns>
  671. private bool GetModeSliceDataNormal()
  672. {
  673. try
  674. {
  675. //分别计算每一行的图像像素值(多线程并行计算)
  676. Parallel.For(0, _generalModelSlice.SliceHeight, _parallelOption, GetSliceData);
  677. return true;
  678. }
  679. catch (Exception e)
  680. {
  681. LogHelper.ErrorLog("SliceHelper, GetModeSliceDataNormal have an error," + e.Message + "," + e.StackTrace);
  682. return false;
  683. }
  684. }
  685. /// <summary>
  686. /// 计算切面图像点像素值,每一行
  687. /// </summary>
  688. /// <param name="start"></param>
  689. /// <param name="end"></param>
  690. private void GetSliceData(int start)
  691. {
  692. try
  693. {
  694. //精度要求
  695. float Precision = 0.0001f;
  696. //精度要求
  697. float NegPrecision = -0.0001f;
  698. var tran = _coordinateTransformation3D.TranslationMatrix;
  699. var rotationMatrix = _coordinateTransformation3D.RotationMatrix;
  700. var startIndex = start * _generalModelSlice.SliceWidth;
  701. var maxCount = startIndex + _generalModelSlice.SliceWidth;
  702. var modelLengthX = _modelSize.ModelLengthX;
  703. var x = tran[0] + rotationMatrix[1] * start;
  704. var y = tran[1] + rotationMatrix[4] * start;
  705. var z = tran[2] + rotationMatrix[7] * start;
  706. var negativeEdge = NegPrecision;
  707. var xEdge = _modelSize.ModelLengthX - 1 + Precision;
  708. var yEdge = _modelSize.ModelLengthY - 1 + Precision;
  709. var zEdge = _modelSize.ModelLengthZ - 1 + Precision;
  710. while (startIndex < maxCount)
  711. {
  712. if (x > negativeEdge && x < xEdge && y > negativeEdge && y < yEdge && z > negativeEdge && z < zEdge)
  713. {
  714. _generalModelSlice.SliceImageData[startIndex] = _modelData[(int)(z + 0.5)][modelLengthX * (int)(y + 0.5) + (int)(x + 0.5)];
  715. }
  716. else
  717. {
  718. _generalModelSlice.SliceImageData[startIndex] = 0;
  719. }
  720. x += rotationMatrix[0];
  721. y += rotationMatrix[3];
  722. z += rotationMatrix[6];
  723. ++startIndex;
  724. }
  725. }
  726. catch (Exception e)
  727. {
  728. LogHelper.ErrorLog($"SliceHelper, GetSliceData error {e}");
  729. }
  730. }
  731. /// <summary>
  732. /// 拷贝模型切片数据到目标byte数组
  733. /// </summary>
  734. private static void CopyPixel(byte[] srcByte, Size srcSize, byte[] dstByte, int dstWidth)
  735. {
  736. for (var h = 0; h < srcSize.Height; ++h)
  737. {
  738. var start1 = h * srcSize.Width;
  739. var start2 = h * dstWidth;
  740. for (var w = 0; w < srcSize.Width; ++w)
  741. {
  742. dstByte[start2] = srcByte[start1];
  743. ++start1;
  744. ++start2;
  745. }
  746. }
  747. }
  748. /// <summary>
  749. /// 返回切面端点的三维坐标
  750. /// </summary>
  751. /// <returns></returns>
  752. public List<Point3DF> GetModelSliceEndPoint3D()
  753. {
  754. return _endPointList3D;
  755. }
  756. /// <summary>
  757. /// 返回切面端点的二维坐标
  758. /// </summary>
  759. /// <returns></returns>
  760. public List<Point2DF> GetModelSliceEndPointPolar2D()
  761. {
  762. return _endPointList2DPolar;
  763. }
  764. /// <summary>
  765. /// 保存切片图像到目标路径
  766. /// </summary>
  767. private bool SaveImage(string savePath)
  768. {
  769. try
  770. {
  771. ////计算目标图像大小
  772. var dstSize = GetDstImageSize();
  773. var byteDst = new byte[dstSize.Width * dstSize.Height];
  774. _sliceWidth2N = MathTools3D.GetMinPowOfTwo(_generalModelSlice.SliceWidth);
  775. _sliceHeight2N = MathTools3D.GetMinPowOfTwo(_generalModelSlice.SliceHeight);
  776. //计算缩放比率
  777. var ratioWidth = _sliceWidth2N / dstSize.Width;
  778. var ratioHeight = _sliceHeight2N / dstSize.Height;
  779. ratioWidth = ratioWidth > 1 ? ratioWidth : 1;
  780. ratioHeight = ratioHeight > 1 ? ratioHeight : 1;
  781. var generalWH = new Size(_generalModelSlice.SliceWidth, _generalModelSlice.SliceHeight);
  782. if (ratioWidth > 1 || ratioHeight > 1)
  783. {
  784. var remainderW = _generalModelSlice.SliceWidth % ratioWidth;
  785. var remainderH = _generalModelSlice.SliceHeight % ratioHeight;
  786. _resizedModelSlice.SliceWidth = (_generalModelSlice.SliceWidth + remainderW) / ratioWidth;
  787. _resizedModelSlice.SliceHeight = (_generalModelSlice.SliceHeight + remainderH) / ratioHeight;
  788. var resizedWH = new Size(_resizedModelSlice.SliceWidth, _resizedModelSlice.SliceHeight);
  789. //缩放并将数据拷贝到最终图像上
  790. _resizedModelSlice.SliceImageData = ResizeNearestNeighbor(_generalModelSlice.SliceImageData, generalWH, resizedWH);
  791. CopyPixel(_resizedModelSlice.SliceImageData, resizedWH, byteDst, dstSize.Width);
  792. }
  793. else
  794. {
  795. var size = new Size(_generalModelSlice.SliceWidth, _generalModelSlice.SliceHeight);
  796. CopyPixel(_generalModelSlice.SliceImageData, size, byteDst, dstSize.Width);
  797. }
  798. //dstImage.Save(savePath);
  799. ByteEncoderSave(byteDst, new Size(dstSize.Width, dstSize.Height), savePath);
  800. //计算图像的极坐标
  801. ChangeToPolarCoordinates(ratioWidth, ratioHeight);
  802. return true;
  803. }
  804. catch (Exception e)
  805. {
  806. LogHelper.ErrorLog("SliceHelper, SaveImage have an error," + e.Message + "," + e.StackTrace);
  807. return false;
  808. }
  809. }
  810. /// <summary>
  811. /// 用最近邻法对图像byte数组进行resize
  812. /// </summary>
  813. /// <param name="dstWidth"></param>
  814. /// <param name="dstHeight"></param>
  815. /// <param name="dstDataBuffer"></param>
  816. public static byte[] ResizeNearestNeighbor(byte[] srcByte, Size srcSize, Size dstSize)
  817. {
  818. int bytesPerPixel = 1;
  819. byte[] dstDataBuffer = new byte[dstSize.Width * dstSize.Height];
  820. int dstDataLen = dstSize.Width * dstSize.Height * bytesPerPixel;
  821. if (dstDataBuffer.Length < dstDataLen)
  822. {
  823. Array.Resize(ref dstDataBuffer, dstDataLen);
  824. }
  825. float scale_x = (float)srcSize.Width / dstSize.Width;
  826. float scale_y = (float)srcSize.Height / dstSize.Height;
  827. int dstStep = dstSize.Width * bytesPerPixel;
  828. int srcStep = srcSize.Width * bytesPerPixel;
  829. int ny, nx, nc;
  830. int sy, sx;
  831. for (ny = 0; ny < dstSize.Height; ++ny)
  832. {
  833. sy = Convert.ToInt32(Math.Floor(ny * scale_y));
  834. sy = Math.Min(sy, srcSize.Height - 1);
  835. for (nx = 0; nx < dstSize.Width; ++nx)
  836. {
  837. sx = Convert.ToInt32(Math.Floor(nx * scale_x));
  838. sx = Math.Min(sx, srcSize.Width - 1);
  839. for (nc = 0; nc < bytesPerPixel; nc++)
  840. {
  841. dstDataBuffer[ny * dstStep + nx * bytesPerPixel + nc] =
  842. srcByte[sy * srcStep + sx * bytesPerPixel + nc];
  843. }
  844. }
  845. }
  846. return dstDataBuffer;
  847. }
  848. public static bool ByteEncoderSave(byte[] rawValues, Size size, string savePath)
  849. {
  850. EnumColorType _colorType = EnumColorType.Gray8;
  851. var decodedPointer = Marshal.UnsafeAddrOfPinnedArrayElement(rawValues, 0);
  852. AIReconstructor.StructImageInfo imageInfo = new AIReconstructor.StructImageInfo(size.Width, size.Height, _colorType, decodedPointer);
  853. AIReconstructor.StructImwriteParam[] imwriteParams = new AIReconstructor.StructImwriteParam[1];
  854. imwriteParams[0] = new AIReconstructor.StructImwriteParam(AIReconstructor.EnumImwriteFlags.JpegQuality, 80);
  855. if (!AIReconstructor.ImDataSave(imageInfo, AIReconstructor.EnumImwriteExtension.Jpg,
  856. imwriteParams, 1, savePath))
  857. {
  858. int errorMaxLen = 256;
  859. StringBuilder errorMsg = new StringBuilder(errorMaxLen);
  860. AIReconstructor.EnumCppCoreErrorCode errorCode = AIReconstructor.EnumCppCoreErrorCode.None;
  861. AIReconstructor.GetErrorCodeAndMsg(ref errorCode, errorMsg, errorMaxLen);
  862. throw new Exception("Failed at encoding model image datas, error code: "
  863. + errorCode.ToString() + " , details: " + errorMsg.ToString() + " .");
  864. }
  865. return true;
  866. }
  867. /// <summary>
  868. /// 根据3个三维坐标点返回其所在的平面
  869. /// </summary>
  870. /// <param name="pointList"></param>
  871. public byte[] GetMeasureImage(List<Point3DF> pointList, out int width, out int height)
  872. {
  873. try
  874. {
  875. CefInputDatas inputData = new CefInputDatas();
  876. inputData.PointsList = pointList;
  877. if (!GetModelSlice(inputData))
  878. {
  879. LogHelper.ErrorLog("Server SliceHelper, GetMeasureImage GetModelSlice failed.");
  880. width = 0;
  881. height = 0;
  882. return null;
  883. }
  884. var decodedPointer = Marshal.UnsafeAddrOfPinnedArrayElement(_generalModelSlice.SliceImageData, 0);
  885. EnumColorType _colorType = EnumColorType.Gray8;
  886. AI.Reconstruction.AIReconstructor.StructImageInfo srcImageInfo = new AI.Reconstruction.AIReconstructor.StructImageInfo(_generalModelSlice.SliceWidth,
  887. _generalModelSlice.SliceHeight,
  888. _colorType,
  889. decodedPointer);
  890. int bytesize = _generalModelSlice.SliceWidth * _generalModelSlice.SliceHeight;
  891. AI.Reconstruction.AIReconstructor.StructImageInfo dstImageInfo = new AI.Reconstruction.AIReconstructor.StructImageInfo();
  892. Get2DMeasureImage(srcImageInfo, bytesize, ref dstImageInfo);
  893. int imageSize = dstImageInfo.Width * dstImageInfo.Height;
  894. byte[] byteArray = new byte[imageSize];
  895. Marshal.Copy(dstImageInfo.DataPointer, byteArray, 0, imageSize);
  896. width = dstImageInfo.Width;
  897. height = dstImageInfo.Height;
  898. //手动释放内存
  899. if (srcImageInfo.DataPointer != IntPtr.Zero)
  900. {
  901. srcImageInfo.DataPointer = IntPtr.Zero;
  902. }
  903. if (dstImageInfo.DataPointer != IntPtr.Zero)
  904. {
  905. dstImageInfo.DataPointer = IntPtr.Zero;
  906. }
  907. return byteArray;
  908. }
  909. catch (Exception e)
  910. {
  911. LogHelper.ErrorLog("Server SliceHelper, GetMeasureImage error," + e.Message + "," + e.StackTrace);
  912. width = 0;
  913. height = 0;
  914. return null;
  915. }
  916. }
  917. }
  918. }