VideoStatusInspect.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.IO;
  6. namespace VideoStatusInspectorCSLib
  7. {
  8. public enum CurrentVideoState
  9. {
  10. Notjudged = 0, //不能判断当前状态
  11. Stationary = 1, //静止状态
  12. Moving = 2, //运行状态
  13. Error = 3, //存在错误
  14. };
  15. public struct ImageDataStructCS
  16. {
  17. public int ImgWidth;
  18. public int ImgHeight;
  19. public EnumColorType ImgColorType;
  20. }
  21. [StructLayout(LayoutKind.Sequential)]
  22. public struct ExtendStateCS
  23. {
  24. [MarshalAs(UnmanagedType.I1)]
  25. public bool LastState; //上一帧状态 false:静止 true :运行
  26. [MarshalAs(UnmanagedType.I1)]
  27. public bool MovingExtendedState; // true: 实际已经判为静置,但强制改为是扫查中的状态
  28. public int MovingExtendedNum; // 实际已经判为静置,但强制改为是扫查中的帧数
  29. public int TrueMovingNum; // 真正的扫查中的帧数(强制改成的扫查中的,不算)
  30. [MarshalAs(UnmanagedType.I1)]
  31. public bool StationaryExtendedState; // true: 实际已经判为扫查中,但强制改为是静置的状态
  32. public int StationaryExtendedNum; // 实际已经判为扫查中,但强制改为是静置状态的帧数
  33. public int TrueStationaryNum; // 真正的静置中的帧数(强制改为静置的,不算)
  34. };
  35. public class VideoStatusInspect
  36. {
  37. [DllImport(@"VideoStatusInspector", CallingConvention = CallingConvention.Cdecl)]
  38. [return: MarshalAs(UnmanagedType.I1)]
  39. internal static extern bool ConvertImage(IntPtr srcImgData, int imgwidth, int imgheight, EnumColorType colorType,
  40. int newW, int newH, IntPtr dstImgData);
  41. [DllImport(@"VideoStatusInspector")]
  42. [return: MarshalAs(UnmanagedType.I1)]
  43. internal static extern bool ProcessOneImage(IntPtr imgData, int imgwidth, int imgheight, EnumColorType colorType, ref double cannyRatio);
  44. [DllImport(@"VideoStatusInspector")]
  45. internal static extern double CompareTwoImage(IntPtr pSrc1, IntPtr pSrc2, int width, int height, int widthstep,
  46. ref double corr, ref double diff);
  47. [DllImport(@"VideoStatusInspector", CallingConvention = CallingConvention.Cdecl)]
  48. internal static extern CurrentVideoState JudgeImage(double[] diffData, double[] corrData, double[] cannyData,
  49. int num, int movingDelayNum, int stationaryDelayNum, int movingDelayTriggerFrames, int stationaryDelayTriggerFrames, bool manuallyUnfreeze,
  50. [MarshalAs(UnmanagedType.Struct)] ref ExtendStateCS state);
  51. private ExtendStateCS m_state;
  52. private ImageDataStructCS m_LastImg; //上一张图像信息
  53. private int m_queueSize = 30;
  54. private int m_movingDelayFrames = 90; // 从moving变为静置的时候,有多少帧被强制转为moving
  55. private int m_stationaryDelayFrames = 5; // 从静置变为moving的时候,有多少帧被强制转为静置
  56. private int m_movingDelayTriggerFrames = 10; // 从moving变为静置时,可以触发延时的条件是前面得有多少帧是moving,否则立即转为静置
  57. private int m_stationaryDelayTriggerFrames = 150; // 从静置变为moving时,可以触发延时的条件是前面得有多少帧是stationary,否则立即转为moving
  58. private volatile bool m_manuallyUnfreeze = false; // 手动解冻
  59. private volatile bool m_enableDebugImageWrite = false;
  60. private int m_width;
  61. private int m_height;
  62. private double m_scale;
  63. private bool m_bLastImg;
  64. private bool m_bWork = false;
  65. private byte[] m_tempData;
  66. private GCHandle m_hObject;
  67. private IntPtr m_pTempData;
  68. private byte[] m_resizedData;
  69. private GCHandle m_resizedObject;
  70. private IntPtr m_pResizedData;
  71. private Queue<double> m_vCorrData = new Queue<double>();
  72. private Queue<double> m_vDiffData = new Queue<double>();
  73. private Queue<double> m_vCannyData = new Queue<double>();
  74. private const string DEBUG_FOLDER_NAME = "AIDebugImages";
  75. /// <summary>
  76. /// 构造函数
  77. /// </summary>
  78. public VideoStatusInspect()
  79. {
  80. }
  81. /// <summary>
  82. /// 设置延时参数
  83. /// </summary>
  84. /// <param name="workingStateDelayFrames">判断是否在扫查中的延时帧数(默认参数是90)
  85. /// 设成90意味着,如果之前是在扫查中(返回结果为Moving),当医生开始不再扫查后,90帧后返回结果才变成Stationary)</param>
  86. public void SetWorkingStateDelayFrames(int workingStateDelayFrames)
  87. {
  88. m_movingDelayFrames = workingStateDelayFrames;
  89. }
  90. public void ManuallyUnfreeze()
  91. {
  92. m_manuallyUnfreeze = true;
  93. }
  94. public void EnableDebugImagesWrite(bool enable)
  95. {
  96. m_enableDebugImageWrite = enable;
  97. }
  98. /// <summary>
  99. /// 销毁
  100. /// </summary>
  101. public void Dispose()
  102. {
  103. if (m_hObject.IsAllocated)
  104. {
  105. m_hObject.Free();
  106. }
  107. if (m_tempData != null)
  108. {
  109. m_pTempData = IntPtr.Zero;
  110. Array.Clear(m_tempData, 0, m_tempData.Length);
  111. m_tempData = null;
  112. }
  113. if (m_resizedObject.IsAllocated)
  114. {
  115. m_resizedObject.Free();
  116. }
  117. if (m_resizedData != null)
  118. {
  119. m_pResizedData = IntPtr.Zero;
  120. Array.Clear(m_resizedData, 0, m_resizedData.Length);
  121. m_resizedData = null;
  122. }
  123. }
  124. /// <summary>
  125. /// 传入一帧图进行检测
  126. /// </summary>
  127. /// <param name="srcImg">待测图像</param>
  128. /// <returns>当前时刻是否在扫查中</returns>
  129. public CurrentVideoState ImgDataCalculateCS(RawImage srcImg)
  130. {
  131. if (!CheckInputImageInfo(srcImg.Width, srcImg.Height, srcImg.ColorType))
  132. {
  133. return CurrentVideoState.Error;
  134. }
  135. if (m_enableDebugImageWrite)
  136. {
  137. if (!Directory.Exists(DEBUG_FOLDER_NAME))
  138. {
  139. Directory.CreateDirectory(DEBUG_FOLDER_NAME);
  140. }
  141. string fileName = "RawImage_"+ DateTime.Now.ToString("yyyyMMdd_HHmm_ssffff");
  142. string filePath = System.IO.Path.Combine(DEBUG_FOLDER_NAME, fileName + ".txt");
  143. RawImage.SaveRawImageIntoFile(srcImg, filePath);
  144. }
  145. return ImageTransport(srcImg, 1);
  146. }
  147. public CurrentVideoState ImgDataCalculateCS(IntptrRawImage srcImg)
  148. {
  149. if (!CheckInputImageInfo(srcImg.Width, srcImg.Height, srcImg.ColorType))
  150. {
  151. return CurrentVideoState.Error;
  152. }
  153. if (m_enableDebugImageWrite)
  154. {
  155. if (!Directory.Exists(DEBUG_FOLDER_NAME))
  156. {
  157. Directory.CreateDirectory(DEBUG_FOLDER_NAME);
  158. }
  159. string fileName = "IntptrRawImage_"+DateTime.Now.ToString("yyyyMMdd_HHmm_ssffff");
  160. string filePath = System.IO.Path.Combine(DEBUG_FOLDER_NAME, fileName + ".txt");
  161. int dataLength = srcImg.BytesPerPixel * srcImg.Width * srcImg.Height;
  162. byte[] datas = new byte[dataLength];
  163. Marshal.Copy(srcImg.DataPointer, datas, 0, dataLength);
  164. RawImage image = new RawImage(datas, srcImg.Width, srcImg.Height, srcImg.ColorType);
  165. RawImage.SaveRawImageIntoFile(image, filePath);
  166. }
  167. return ImageTransport(srcImg, 1);
  168. }
  169. /// <summary>
  170. /// 参数设置
  171. /// </summary>
  172. private void SetParameterCS()
  173. {
  174. m_bLastImg = false;
  175. m_vCorrData.Clear();
  176. m_vDiffData.Clear();
  177. m_vCannyData.Clear();
  178. double height = (double)(m_LastImg.ImgHeight);
  179. double width = (double)(m_LastImg.ImgWidth);
  180. double diagonal = Math.Sqrt(width * height);
  181. double m_scale = 0.5;
  182. if (diagonal > 300)
  183. {
  184. m_scale = 300 / diagonal;
  185. }
  186. else
  187. {
  188. m_scale = 1;
  189. }
  190. // 如果长宽比不夸张,则直接等比例缩放长和宽
  191. if (width > 0.5 * height && width < 2 * height)
  192. {
  193. m_height = (int)(height * m_scale / 4) * 4;
  194. m_width = (int)(width * m_scale / 4) * 4;
  195. }
  196. else
  197. {
  198. // 缩放后短边不要小于200
  199. if (width >= height)
  200. {
  201. m_height = Math.Max((int)(height * m_scale / 4) * 4, 200);
  202. m_width = (int)(m_height * 1.5);
  203. }
  204. else
  205. {
  206. m_width = Math.Max((int)(width * m_scale / 4) * 4, 200);
  207. m_height = (int)(m_width * 1.5);
  208. }
  209. }
  210. if (m_hObject.IsAllocated)
  211. {
  212. m_hObject.Free();
  213. }
  214. if (m_tempData != null)
  215. {
  216. m_pTempData = IntPtr.Zero;
  217. Array.Clear(m_tempData, 0, m_tempData.Length);
  218. m_tempData = null;
  219. }
  220. m_tempData = new byte[m_width * m_height];
  221. m_hObject = GCHandle.Alloc(m_tempData, GCHandleType.Pinned);
  222. m_pTempData = m_hObject.AddrOfPinnedObject();
  223. if (m_resizedObject.IsAllocated)
  224. {
  225. m_resizedObject.Free();
  226. }
  227. if (m_resizedData != null)
  228. {
  229. m_pResizedData = IntPtr.Zero;
  230. Array.Clear(m_resizedData, 0, m_resizedData.Length);
  231. m_resizedData = null;
  232. }
  233. m_resizedData = new byte[m_width * m_height];
  234. m_resizedObject = GCHandle.Alloc(m_resizedData, GCHandleType.Pinned);
  235. m_pResizedData = m_resizedObject.AddrOfPinnedObject();
  236. m_state.LastState = false;
  237. m_state.MovingExtendedState = false;
  238. m_state.MovingExtendedNum = 0;
  239. m_state.StationaryExtendedState = false;
  240. m_state.StationaryExtendedNum = 0;
  241. m_state.TrueMovingNum = 0;
  242. m_state.TrueStationaryNum = 0;
  243. }
  244. private bool CheckInputImageInfo(int imgWidth, int imgHeight, EnumColorType colorType)
  245. {
  246. // 为了避免求resize尺寸时除以0,如果图像尺寸为0,直接先退出
  247. if (imgWidth <= 0 || imgHeight <= 0)
  248. {
  249. return false;
  250. }
  251. if (!m_bWork) //认为是第一张数据
  252. {
  253. m_LastImg.ImgWidth = imgWidth;
  254. m_LastImg.ImgHeight = imgHeight;
  255. m_LastImg.ImgColorType = colorType;
  256. SetParameterCS();
  257. m_bWork = true;
  258. }
  259. else
  260. {
  261. //如果当前参数和上一帧的参数不同,认为当前图像为新视频的第一帧,重新设置参数
  262. if (m_LastImg.ImgWidth != imgWidth || m_LastImg.ImgHeight != imgHeight || m_LastImg.ImgColorType != colorType)
  263. {
  264. m_LastImg.ImgWidth = imgWidth;
  265. m_LastImg.ImgHeight = imgHeight;
  266. m_LastImg.ImgColorType = colorType;
  267. SetParameterCS();
  268. }
  269. }
  270. if (m_LastImg.ImgColorType != EnumColorType.Gray8 && m_LastImg.ImgColorType != EnumColorType.Bgr &&
  271. m_LastImg.ImgColorType != EnumColorType.Bgra && m_LastImg.ImgColorType != EnumColorType.Rgb &&
  272. m_LastImg.ImgColorType != EnumColorType.Rgba)
  273. {
  274. return false;
  275. }
  276. return true;
  277. }
  278. private CurrentVideoState ImageTransport(RawImage srcImg, int imgCount)
  279. {
  280. CurrentVideoState result = CurrentVideoState.Notjudged;
  281. if (srcImg.Width <= 0 || srcImg.Height <= 0)
  282. {
  283. return CurrentVideoState.Error;
  284. }
  285. if (srcImg.ColorType != EnumColorType.Gray8 && srcImg.ColorType != EnumColorType.Bgr &&
  286. srcImg.ColorType != EnumColorType.Bgra && srcImg.ColorType != EnumColorType.Rgb &&
  287. srcImg.ColorType != EnumColorType.Rgba)
  288. {
  289. return CurrentVideoState.Error;
  290. }
  291. if (srcImg.DataBuffer == null)
  292. {
  293. return CurrentVideoState.Error;
  294. }
  295. GCHandle hObjectSrc = GCHandle.Alloc(srcImg.DataBuffer, GCHandleType.Pinned);
  296. IntPtr pSrcData = hObjectSrc.AddrOfPinnedObject();
  297. ConvertImage(pSrcData, srcImg.Width, srcImg.Height, srcImg.ColorType, m_width, m_height ,m_pResizedData);
  298. if (!m_bLastImg)
  299. {
  300. m_resizedData.CopyTo(m_tempData, 0);
  301. m_bLastImg = true;
  302. }
  303. else
  304. {
  305. double cannyRatio = 0;
  306. ProcessOneImage(m_pResizedData, m_width, m_height, EnumColorType.Gray8, ref cannyRatio);
  307. m_vCannyData.Enqueue(cannyRatio);
  308. double corr = 0, diff = 0;
  309. double ratio = CompareTwoImage(m_pTempData, m_pResizedData, m_width, m_height, m_width, ref corr, ref diff);
  310. m_vDiffData.Enqueue(diff);
  311. m_vCorrData.Enqueue(corr);
  312. // 队列数值大于阈值则删除0号元素
  313. if (m_vCorrData.Count > m_queueSize)
  314. {
  315. m_vCorrData.Dequeue();
  316. m_vDiffData.Dequeue();
  317. m_vCannyData.Dequeue();
  318. }
  319. m_resizedData.CopyTo(m_tempData, 0);
  320. }
  321. if (hObjectSrc.IsAllocated)
  322. {
  323. hObjectSrc.Free();
  324. }
  325. result = JudgeVideoCurrentStatusCS();
  326. return result;
  327. }
  328. private CurrentVideoState ImageTransport(IntptrRawImage srcImg, int imgCount)
  329. {
  330. CurrentVideoState result = CurrentVideoState.Notjudged;
  331. if (srcImg.Width <= 0 || srcImg.Height <= 0)
  332. {
  333. return CurrentVideoState.Error;
  334. }
  335. if (srcImg.ColorType != EnumColorType.Gray8 && srcImg.ColorType != EnumColorType.Bgr &&
  336. srcImg.ColorType != EnumColorType.Bgra && srcImg.ColorType != EnumColorType.Rgb &&
  337. srcImg.ColorType != EnumColorType.Rgba)
  338. {
  339. return CurrentVideoState.Error;
  340. }
  341. if (srcImg.DataPointer == null || srcImg.DataPointer == IntPtr.Zero)
  342. {
  343. return CurrentVideoState.Error;
  344. }
  345. ConvertImage(srcImg.DataPointer, srcImg.Width, srcImg.Height, srcImg.ColorType, m_width, m_height, m_pResizedData);
  346. if (!m_bLastImg)
  347. {
  348. m_resizedData.CopyTo(m_tempData, 0);
  349. m_bLastImg = true;
  350. }
  351. else
  352. {
  353. double cannyRatio = 0, entropy = 0;
  354. ProcessOneImage(m_pResizedData, m_width, m_height, EnumColorType.Gray8, ref cannyRatio);
  355. m_vCannyData.Enqueue(cannyRatio);
  356. double corr = 0, diff = 0;
  357. double ratio = CompareTwoImage(m_pTempData, m_pResizedData, m_width, m_height, m_width, ref corr, ref diff);
  358. m_vDiffData.Enqueue(diff);
  359. m_vCorrData.Enqueue(corr);
  360. // 队列数值大于阈值则删除0号元素
  361. if (m_vCorrData.Count > m_queueSize)
  362. {
  363. m_vCorrData.Dequeue();
  364. m_vDiffData.Dequeue();
  365. m_vCannyData.Dequeue();
  366. }
  367. m_resizedData.CopyTo(m_tempData, 0);
  368. }
  369. result = JudgeVideoCurrentStatusCS();
  370. return result;
  371. }
  372. private CurrentVideoState JudgeVideoCurrentStatusCS()
  373. {
  374. CurrentVideoState eRet = CurrentVideoState.Notjudged;
  375. int num = m_vDiffData.Count();
  376. if (num > m_queueSize - 1)
  377. {
  378. double[] diffData = m_vDiffData.ToArray();
  379. double[] corrData = m_vCorrData.ToArray();
  380. double[] cannyData = m_vCannyData.ToArray();
  381. eRet = JudgeImage(diffData, corrData, cannyData,num, m_movingDelayFrames, m_stationaryDelayFrames,
  382. m_movingDelayTriggerFrames, m_stationaryDelayTriggerFrames,m_manuallyUnfreeze, ref m_state);
  383. m_manuallyUnfreeze = false;
  384. }
  385. return eRet;
  386. }
  387. }
  388. }