using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.IO; namespace VideoStatusInspectorCSLib { public enum CurrentVideoState { Notjudged = 0, //不能判断当前状态 Stationary = 1, //静止状态 Moving = 2, //运行状态 Error = 3, //存在错误 }; public struct ImageDataStructCS { public int ImgWidth; public int ImgHeight; public EnumColorType ImgColorType; } [StructLayout(LayoutKind.Sequential)] public struct ExtendStateCS { [MarshalAs(UnmanagedType.I1)] public bool LastState; //上一帧状态 false:静止 true :运行 [MarshalAs(UnmanagedType.I1)] public bool MovingExtendedState; // true: 实际已经判为静置,但强制改为是扫查中的状态 public int MovingExtendedNum; // 实际已经判为静置,但强制改为是扫查中的帧数 public int TrueMovingNum; // 真正的扫查中的帧数(强制改成的扫查中的,不算) [MarshalAs(UnmanagedType.I1)] public bool StationaryExtendedState; // true: 实际已经判为扫查中,但强制改为是静置的状态 public int StationaryExtendedNum; // 实际已经判为扫查中,但强制改为是静置状态的帧数 public int TrueStationaryNum; // 真正的静置中的帧数(强制改为静置的,不算) }; public class VideoStatusInspect { [DllImport(@"VideoStatusInspector", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool ConvertImage(IntPtr srcImgData, int imgwidth, int imgheight, EnumColorType colorType, int newW, int newH, IntPtr dstImgData); [DllImport(@"VideoStatusInspector")] [return: MarshalAs(UnmanagedType.I1)] internal static extern bool ProcessOneImage(IntPtr imgData, int imgwidth, int imgheight, EnumColorType colorType, ref double cannyRatio); [DllImport(@"VideoStatusInspector")] internal static extern double CompareTwoImage(IntPtr pSrc1, IntPtr pSrc2, int width, int height, int widthstep, ref double corr, ref double diff); [DllImport(@"VideoStatusInspector", CallingConvention = CallingConvention.Cdecl)] internal static extern CurrentVideoState JudgeImage(double[] diffData, double[] corrData, double[] cannyData, int num, int movingDelayNum, int stationaryDelayNum, int movingDelayTriggerFrames, int stationaryDelayTriggerFrames, bool manuallyUnfreeze, [MarshalAs(UnmanagedType.Struct)] ref ExtendStateCS state); private ExtendStateCS m_state; private ImageDataStructCS m_LastImg; //上一张图像信息 private int m_queueSize = 30; private int m_movingDelayFrames = 90; // 从moving变为静置的时候,有多少帧被强制转为moving private int m_stationaryDelayFrames = 5; // 从静置变为moving的时候,有多少帧被强制转为静置 private int m_movingDelayTriggerFrames = 10; // 从moving变为静置时,可以触发延时的条件是前面得有多少帧是moving,否则立即转为静置 private int m_stationaryDelayTriggerFrames = 150; // 从静置变为moving时,可以触发延时的条件是前面得有多少帧是stationary,否则立即转为moving private volatile bool m_manuallyUnfreeze = false; // 手动解冻 private volatile bool m_enableDebugImageWrite = false; private int m_width; private int m_height; private double m_scale; private bool m_bLastImg; private bool m_bWork = false; private byte[] m_tempData; private GCHandle m_hObject; private IntPtr m_pTempData; private byte[] m_resizedData; private GCHandle m_resizedObject; private IntPtr m_pResizedData; private Queue m_vCorrData = new Queue(); private Queue m_vDiffData = new Queue(); private Queue m_vCannyData = new Queue(); private const string DEBUG_FOLDER_NAME = "AIDebugImages"; /// /// 构造函数 /// public VideoStatusInspect() { } /// /// 设置延时参数 /// /// 判断是否在扫查中的延时帧数(默认参数是90) /// 设成90意味着,如果之前是在扫查中(返回结果为Moving),当医生开始不再扫查后,90帧后返回结果才变成Stationary) public void SetWorkingStateDelayFrames(int workingStateDelayFrames) { m_movingDelayFrames = workingStateDelayFrames; } public void ManuallyUnfreeze() { m_manuallyUnfreeze = true; } public void EnableDebugImagesWrite(bool enable) { m_enableDebugImageWrite = enable; } /// /// 销毁 /// public void Dispose() { if (m_hObject.IsAllocated) { m_hObject.Free(); } if (m_tempData != null) { m_pTempData = IntPtr.Zero; Array.Clear(m_tempData, 0, m_tempData.Length); m_tempData = null; } if (m_resizedObject.IsAllocated) { m_resizedObject.Free(); } if (m_resizedData != null) { m_pResizedData = IntPtr.Zero; Array.Clear(m_resizedData, 0, m_resizedData.Length); m_resizedData = null; } } /// /// 传入一帧图进行检测 /// /// 待测图像 /// 当前时刻是否在扫查中 public CurrentVideoState ImgDataCalculateCS(RawImage srcImg) { if (!CheckInputImageInfo(srcImg.Width, srcImg.Height, srcImg.ColorType)) { return CurrentVideoState.Error; } if (m_enableDebugImageWrite) { if (!Directory.Exists(DEBUG_FOLDER_NAME)) { Directory.CreateDirectory(DEBUG_FOLDER_NAME); } string fileName = "RawImage_"+ DateTime.Now.ToString("yyyyMMdd_HHmm_ssffff"); string filePath = System.IO.Path.Combine(DEBUG_FOLDER_NAME, fileName + ".txt"); RawImage.SaveRawImageIntoFile(srcImg, filePath); } return ImageTransport(srcImg, 1); } public CurrentVideoState ImgDataCalculateCS(IntptrRawImage srcImg) { if (!CheckInputImageInfo(srcImg.Width, srcImg.Height, srcImg.ColorType)) { return CurrentVideoState.Error; } if (m_enableDebugImageWrite) { if (!Directory.Exists(DEBUG_FOLDER_NAME)) { Directory.CreateDirectory(DEBUG_FOLDER_NAME); } string fileName = "IntptrRawImage_"+DateTime.Now.ToString("yyyyMMdd_HHmm_ssffff"); string filePath = System.IO.Path.Combine(DEBUG_FOLDER_NAME, fileName + ".txt"); int dataLength = srcImg.BytesPerPixel * srcImg.Width * srcImg.Height; byte[] datas = new byte[dataLength]; Marshal.Copy(srcImg.DataPointer, datas, 0, dataLength); RawImage image = new RawImage(datas, srcImg.Width, srcImg.Height, srcImg.ColorType); RawImage.SaveRawImageIntoFile(image, filePath); } return ImageTransport(srcImg, 1); } /// /// 参数设置 /// private void SetParameterCS() { m_bLastImg = false; m_vCorrData.Clear(); m_vDiffData.Clear(); m_vCannyData.Clear(); double height = (double)(m_LastImg.ImgHeight); double width = (double)(m_LastImg.ImgWidth); double diagonal = Math.Sqrt(width * height); double m_scale = 0.5; if (diagonal > 300) { m_scale = 300 / diagonal; } else { m_scale = 1; } // 如果长宽比不夸张,则直接等比例缩放长和宽 if (width > 0.5 * height && width < 2 * height) { m_height = (int)(height * m_scale / 4) * 4; m_width = (int)(width * m_scale / 4) * 4; } else { // 缩放后短边不要小于200 if (width >= height) { m_height = Math.Max((int)(height * m_scale / 4) * 4, 200); m_width = (int)(m_height * 1.5); } else { m_width = Math.Max((int)(width * m_scale / 4) * 4, 200); m_height = (int)(m_width * 1.5); } } if (m_hObject.IsAllocated) { m_hObject.Free(); } if (m_tempData != null) { m_pTempData = IntPtr.Zero; Array.Clear(m_tempData, 0, m_tempData.Length); m_tempData = null; } m_tempData = new byte[m_width * m_height]; m_hObject = GCHandle.Alloc(m_tempData, GCHandleType.Pinned); m_pTempData = m_hObject.AddrOfPinnedObject(); if (m_resizedObject.IsAllocated) { m_resizedObject.Free(); } if (m_resizedData != null) { m_pResizedData = IntPtr.Zero; Array.Clear(m_resizedData, 0, m_resizedData.Length); m_resizedData = null; } m_resizedData = new byte[m_width * m_height]; m_resizedObject = GCHandle.Alloc(m_resizedData, GCHandleType.Pinned); m_pResizedData = m_resizedObject.AddrOfPinnedObject(); m_state.LastState = false; m_state.MovingExtendedState = false; m_state.MovingExtendedNum = 0; m_state.StationaryExtendedState = false; m_state.StationaryExtendedNum = 0; m_state.TrueMovingNum = 0; m_state.TrueStationaryNum = 0; } private bool CheckInputImageInfo(int imgWidth, int imgHeight, EnumColorType colorType) { // 为了避免求resize尺寸时除以0,如果图像尺寸为0,直接先退出 if (imgWidth <= 0 || imgHeight <= 0) { return false; } if (!m_bWork) //认为是第一张数据 { m_LastImg.ImgWidth = imgWidth; m_LastImg.ImgHeight = imgHeight; m_LastImg.ImgColorType = colorType; SetParameterCS(); m_bWork = true; } else { //如果当前参数和上一帧的参数不同,认为当前图像为新视频的第一帧,重新设置参数 if (m_LastImg.ImgWidth != imgWidth || m_LastImg.ImgHeight != imgHeight || m_LastImg.ImgColorType != colorType) { m_LastImg.ImgWidth = imgWidth; m_LastImg.ImgHeight = imgHeight; m_LastImg.ImgColorType = colorType; SetParameterCS(); } } if (m_LastImg.ImgColorType != EnumColorType.Gray8 && m_LastImg.ImgColorType != EnumColorType.Bgr && m_LastImg.ImgColorType != EnumColorType.Bgra && m_LastImg.ImgColorType != EnumColorType.Rgb && m_LastImg.ImgColorType != EnumColorType.Rgba) { return false; } return true; } private CurrentVideoState ImageTransport(RawImage srcImg, int imgCount) { CurrentVideoState result = CurrentVideoState.Notjudged; if (srcImg.Width <= 0 || srcImg.Height <= 0) { return CurrentVideoState.Error; } if (srcImg.ColorType != EnumColorType.Gray8 && srcImg.ColorType != EnumColorType.Bgr && srcImg.ColorType != EnumColorType.Bgra && srcImg.ColorType != EnumColorType.Rgb && srcImg.ColorType != EnumColorType.Rgba) { return CurrentVideoState.Error; } if (srcImg.DataBuffer == null) { return CurrentVideoState.Error; } GCHandle hObjectSrc = GCHandle.Alloc(srcImg.DataBuffer, GCHandleType.Pinned); IntPtr pSrcData = hObjectSrc.AddrOfPinnedObject(); ConvertImage(pSrcData, srcImg.Width, srcImg.Height, srcImg.ColorType, m_width, m_height ,m_pResizedData); if (!m_bLastImg) { m_resizedData.CopyTo(m_tempData, 0); m_bLastImg = true; } else { double cannyRatio = 0; ProcessOneImage(m_pResizedData, m_width, m_height, EnumColorType.Gray8, ref cannyRatio); m_vCannyData.Enqueue(cannyRatio); double corr = 0, diff = 0; double ratio = CompareTwoImage(m_pTempData, m_pResizedData, m_width, m_height, m_width, ref corr, ref diff); m_vDiffData.Enqueue(diff); m_vCorrData.Enqueue(corr); // 队列数值大于阈值则删除0号元素 if (m_vCorrData.Count > m_queueSize) { m_vCorrData.Dequeue(); m_vDiffData.Dequeue(); m_vCannyData.Dequeue(); } m_resizedData.CopyTo(m_tempData, 0); } if (hObjectSrc.IsAllocated) { hObjectSrc.Free(); } result = JudgeVideoCurrentStatusCS(); return result; } private CurrentVideoState ImageTransport(IntptrRawImage srcImg, int imgCount) { CurrentVideoState result = CurrentVideoState.Notjudged; if (srcImg.Width <= 0 || srcImg.Height <= 0) { return CurrentVideoState.Error; } if (srcImg.ColorType != EnumColorType.Gray8 && srcImg.ColorType != EnumColorType.Bgr && srcImg.ColorType != EnumColorType.Bgra && srcImg.ColorType != EnumColorType.Rgb && srcImg.ColorType != EnumColorType.Rgba) { return CurrentVideoState.Error; } if (srcImg.DataPointer == null || srcImg.DataPointer == IntPtr.Zero) { return CurrentVideoState.Error; } ConvertImage(srcImg.DataPointer, srcImg.Width, srcImg.Height, srcImg.ColorType, m_width, m_height, m_pResizedData); if (!m_bLastImg) { m_resizedData.CopyTo(m_tempData, 0); m_bLastImg = true; } else { double cannyRatio = 0, entropy = 0; ProcessOneImage(m_pResizedData, m_width, m_height, EnumColorType.Gray8, ref cannyRatio); m_vCannyData.Enqueue(cannyRatio); double corr = 0, diff = 0; double ratio = CompareTwoImage(m_pTempData, m_pResizedData, m_width, m_height, m_width, ref corr, ref diff); m_vDiffData.Enqueue(diff); m_vCorrData.Enqueue(corr); // 队列数值大于阈值则删除0号元素 if (m_vCorrData.Count > m_queueSize) { m_vCorrData.Dequeue(); m_vDiffData.Dequeue(); m_vCannyData.Dequeue(); } m_resizedData.CopyTo(m_tempData, 0); } result = JudgeVideoCurrentStatusCS(); return result; } private CurrentVideoState JudgeVideoCurrentStatusCS() { CurrentVideoState eRet = CurrentVideoState.Notjudged; int num = m_vDiffData.Count(); if (num > m_queueSize - 1) { double[] diffData = m_vDiffData.ToArray(); double[] corrData = m_vCorrData.ToArray(); double[] cannyData = m_vCannyData.ToArray(); eRet = JudgeImage(diffData, corrData, cannyData,num, m_movingDelayFrames, m_stationaryDelayFrames, m_movingDelayTriggerFrames, m_stationaryDelayTriggerFrames,m_manuallyUnfreeze, ref m_state); m_manuallyUnfreeze = false; } return eRet; } } }