using System; using AForge.Video; using System.Drawing; using AForge.Controls; using System.Threading; using System.Windows.Forms; using AForge.Video.DirectShow; using System.Collections.Generic; namespace shjxCamera { public class WebCamera2 { #region private fields private Bitmap _currFrame; #endregion #region private properties public VideoCaptureDevice VidCapDevice2 { get; set; } private VideoSourcePlayer VidPlayer2 { get; set; } /// /// 当前画面,相机开启时有效 /// public Bitmap CurrFrame { get => VidCapDevice2 is not null ? _currFrame.Clone() as Bitmap : null; protected set => _currFrame = value; } /// /// 摄像头是否已打开 /// public bool IsOpened { get { bool isOpened = false; if (VidPlayer2 == null) { return isOpened; } if (VidPlayer2.InvokeRequired) { VidPlayer2.Invoke(new Action(() => { isOpened = VidPlayer2.IsRunning; })); } else { isOpened = VidPlayer2.IsRunning; } return isOpened; } } #endregion #region static private static FilterInfoCollection vidCollection; public static FilterInfoCollection GetCameras() { vidCollection = new(FilterCategory.VideoInputDevice); return vidCollection; } public static string GetCameraMoniker(int index) { return vidCollection[index].MonikerString; } public static string GetCameraName(int index) { return vidCollection[index].Name; } #endregion #region events /// /// 画面刷新事件 /// public event NewFrameEventHandler NewFrameEvent; #endregion #region 构造函数 /// /// 初始化摄像头 /// /// 摄像头名称 public WebCamera2(string cameraMoniker) { VidCapDevice2 = new VideoCaptureDevice(cameraMoniker); VidCapDevice2.NewFrame += GetCurrFrame; VidPlayer2 = new VideoSourcePlayer { VideoSource = VidCapDevice2 }; _currFrame = null; } /// /// 初始化摄像头 /// public WebCamera2() { VidCapDevice2 = new VideoCaptureDevice(); VidPlayer2 = new VideoSourcePlayer { VideoSource = VidCapDevice2 }; _currFrame = null; } #endregion #region Camera Operate /// /// 连接指定的摄像头,并打开视频信号 /// /// /// public bool OpenCamera(string cameraMoniker) { this.VidCapDevice2.Source = cameraMoniker; VidCapDevice2.NewFrame += this.GetCurrFrame; this.VidCapDevice2.Start(); return this.VidCapDevice2.IsRunning; } /// /// 打开摄像头 /// /// public bool OpenCamera() { if (VidCapDevice2 != null && VidCapDevice2.IsRunning == false) { VidCapDevice2.NewFrame += NewFrameEvent; VidCapDevice2.Start(); } if (VidPlayer2.InvokeRequired) { VidPlayer2.Invoke(new Action(() => { if (VidPlayer2 != null && !VidPlayer2.IsRunning) { VidPlayer2.Start(); } })); } else { if (VidPlayer2 != null && !VidPlayer2.IsRunning) { VidPlayer2.Start(); } } return true; } /// /// 关闭摄像头 /// public void CloseCamera() { if (VidCapDevice2 != null && VidCapDevice2.IsRunning) { VidCapDevice2.SignalToStop(); VidCapDevice2.WaitForStop(); //VidCapDevice.Stop(); VidCapDevice2.NewFrame -= NewFrameEvent; } if (VidPlayer2.InvokeRequired) { VidPlayer2.Invoke(new Action(() => { if (VidPlayer2 == null || !VidPlayer2.IsRunning) return; VidPlayer2.SignalToStop(); VidPlayer2.WaitForStop(); })); } else { if (VidPlayer2 == null || !VidPlayer2.IsRunning) return; VidPlayer2.SignalToStop(); VidPlayer2.WaitForStop(); } } /// /// 拍照 /// /// public Bitmap GrabImage2() { Bitmap bmp = null; if (VidPlayer2.InvokeRequired) { VidPlayer2.Invoke(new Action(() => { if (VidPlayer2.IsRunning) { bmp = VidPlayer2.GetCurrentVideoFrame(); } else { // 探头未连接,重试 int cnt = 0; // 重试10秒 while (!VidPlayer2.IsRunning && cnt < 100) { VidPlayer2.Start(); Thread.Sleep(100); cnt++; //PublicDatas.LogProgram.Error("摄像头未连接,正在重试" + cnt.ToString()); } Thread.Sleep(300); bmp = VidPlayer2.GetCurrentVideoFrame(); } })); } else { if (VidPlayer2.IsRunning) { bmp = VidPlayer2.GetCurrentVideoFrame(); } else { // 探头未连接,重试 int cnt = 0; // 重试10秒 while (!VidPlayer2.IsRunning && cnt < 100) { VidPlayer2.Start(); Thread.Sleep(100); cnt++; // PublicDatas.LogProgram.Error("摄像头未连接,正在重试" + cnt.ToString()); } Thread.Sleep(300); bmp = VidPlayer2.GetCurrentVideoFrame(); } } return bmp; } #endregion #region public methods /// /// 设置显示摄像头图像区域 /// /// /// /// /// /// public void ShowControl(ref Panel parentControl, int left, int top, int width, int height) { if (VidPlayer2 == null) return; parentControl.Controls.Add(VidPlayer2); VidPlayer2.Location = new Point(left, top); VidPlayer2.Size = new Size(width, height); VidPlayer2.Visible = true; } /// /// 显示摄像头属性设置窗体 /// /// public void ShowProperties(IntPtr parentWindow) { VidCapDevice2.DisplayPropertyPage(parentWindow); } /// /// 设置相机属性值 /// /// /// /// public void SetProperties(CameraControlProperty prop, int val, CameraControlFlags flags) { VidCapDevice2.SetCameraProperty(prop, val, flags);// 取消自动曝光 } /// /// 读取当前属性值 /// /// /// /// public void GetProperties(CameraControlProperty prop, out int val, out CameraControlFlags flags) { VidCapDevice2.GetCameraProperty(prop, out val, out flags); } /// /// 读取相机参数的取值范围 /// /// /// /// /// /// /// public void GetPropertiesRange(CameraControlProperty prop, out int minValue, out int maxValue, out int stepSize, out int defaultValue, out CameraControlFlags flags) { VidCapDevice2.GetCameraPropertyRange(prop, out minValue, out maxValue, out stepSize, out defaultValue, out flags); } /// /// 设置视频属性值,亮度、对比度、白平衡、增益等 /// /// /// /// public void SetVideoProcAmpProperties(VideoProcAmpProperty prop, int val, VideoProcAmpFlags flags) { VidCapDevice2.SetVideoProcAmpProperty(prop, val, flags); } /// /// 读取当前视频参数 /// /// /// /// public void GetVideoProcAmpProperties(VideoProcAmpProperty prop, out int val, out VideoProcAmpFlags flags) { VidCapDevice2.GetVideoProcAmpProperty(prop, out val, out flags); } /// /// 读取视频参数的取值范围 /// /// /// /// /// /// /// public void GetVideoProcAmpPropertiesRange(VideoProcAmpProperty prop, out int minValue, out int maxValue, out int stepSize, out int defaultValue, out VideoProcAmpFlags flags) { VidCapDevice2.GetVideoProcAmpPropertyRange(prop, out minValue, out maxValue, out stepSize, out defaultValue, out flags); } /// /// 实时将新的画面存入CurrFrame对象中 /// /// /// public void GetCurrFrame(object sender, NewFrameEventArgs e) { try { if (e.Frame != null) { CurrFrame = e.Frame; } } catch (Exception) { // ignored } } #endregion #region 未使用 /// /// 拍照,返回CurrFrame图像 /// /// //public Bitmap GrabImage2() //{ // return CurrFrame; //} /// /// 返回视频支持的分辨率 /// /// public List GetVideoResolution() { List resolution = new List(); foreach (var cap in VidCapDevice2.VideoCapabilities) { resolution.Add(string.Format("{0} x {1}", cap.FrameSize.Width, cap.FrameSize.Height)); } return resolution; } /// /// 设定视频的分辨率,index可以通过GetVideoResolution()获取所有支持的分辨率来确定 /// /// 在所有支持分辨率中的序号 public void SetVideoResolution(int index) { if (VidCapDevice2.VideoCapabilities.Length > index) { VidCapDevice2.VideoResolution = VidCapDevice2.VideoCapabilities[index]; } } #endregion // 抓取一帧图像,截取中间区域,按灰度值过滤后,计算平均RGB值 public ColorPoint CaptureOne(string captureTag, int size, int gray_min, int gray_max) { Bitmap bmp = GrabImage22(); if (bmp is null) { return new(0, 0, 0, captureTag); } int cntLow = 0, cntHigh = 0, cntNormal = 0; int sumR = 0, sumG = 0, sumB = 0; for (int y = bmp.Height / 2 - size / 2; y < bmp.Height / 2 + size / 2; y++) { for (int x = bmp.Width / 2 - size / 2; x < bmp.Width / 2 + size / 2; x++) { Color cc = bmp.GetPixel(x, y); int gray = Math.Min(255, (int)(cc.R * 0.299 + cc.G * 0.587 + cc.B * 0.114)); if (gray < gray_min) { cntLow++; continue; } if (gray > gray_max) { cntHigh++; continue; } cntNormal++; sumR += cc.R; sumG += cc.G; sumB += cc.B; } } int avgR = 0, avgG = 0, avgB = 0; if (cntNormal > 0) { avgR = sumR / cntNormal; avgG = sumG / cntNormal; avgB = sumB / cntNormal; } // 记录颜色采样信息 ColorPoint cp = new(avgR, avgG, avgB, captureTag); float total = size * size; if (!"watch".Equals(captureTag)) { //logger.LogTrace($"<{captureTag}> HSB({cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}), RGBL({cp.R,3},{cp.G,3},{cp.B,3},{cp.Gray,3}), PLMH({total},{cntLow / total:F3},{cntNormal / total:F3},{cntHigh / total:F3}), WH:({bmp.Width},{bmp.Height})"); //bmp.Save($"Data/{captureTag}.jpg", ImageFormat.Jpeg); } return cp; } // 背景底色采样 public ColorPoint SampleBackground(string taskTag, int threshold_gray, int threshold_hue) { List cps = new List(); for (int i = 0; i < 99; i++) { cps.Add(Sample($"{taskTag}/000S-{i + 1:D2}", threshold_gray, threshold_hue)); if (cps.Count >= 5) { // 如排序后前三轮或后三轮的色调偏差小于阈值,完成采样 ColorPoint[] ar = cps.ToArray(); Array.Sort(ar); //Array.Reverse(ar); if (Math.Abs(ar[0].Hue - ar[2].Hue) <= threshold_hue || Math.Abs(ar[2].Hue - ar[4].Hue) <= threshold_hue) { ColorPoint cp = ar[2]; return cp; } // 未达标,丢弃最早抓取的帧,继续采样 cps.RemoveAt(0); } Thread.Sleep(100); } // 背景底色采样达到最大次数 //logger.LogWarning($"<{taskTag}> 背景底色采样达到最大次数!"); return cps.Last(); } // 色值采样(抓取n帧图像合并计算) public ColorPoint Sample(string prefix, int threshold_gray, int threshold_hue) { int gray_min = threshold_gray; int gray_max = 255 - threshold_gray; List cps = new List(); for (int i = 0; i < 99; i++) { string captureTag = $"{prefix}-{i + 1:D2}"; int size = 200; ColorPoint cp = CaptureOne(captureTag, size, gray_min, gray_max); // 丢弃亮度过低的帧,继续采样 if (cp.Gray <= gray_min) { Thread.Sleep(100); continue; } cps.Add(cp); if (cps.Count >= 5) { // 构建排列的数组 ColorPoint[] ar = cps.ToArray(); Array.Sort(ar); //Array.Reverse(ar); // 前三帧色调偏差小于等于阈值,取前三帧完成采样 if (Math.Abs(ar[0].Hue - ar[2].Hue) <= threshold_hue) { return Merge(ar.Take(3).ToList(), prefix); } // 后三帧色调偏差小于等于阈值,取后三帧完成采样 if (Math.Abs(ar[2].Hue - ar[4].Hue) <= threshold_hue) { return Merge(ar.Skip(2).Take(3).ToList(), prefix); } // 未达标,丢弃最早抓取的帧,继续采样 cps.RemoveAt(0); } Thread.Sleep(100); } // 达到最大采样次数限制,返回最后一次采样结果 //logger.LogWarning($"<{prefix}> 色值采样达到最大次数!"); if (cps.Count > 0) { return cps.Last(); } return new ColorPoint(0, 0, 0, prefix); } // 合并计算色值 private static ColorPoint Merge(List list, string prefix) { int n = list.Count; if (n == 0) { return new ColorPoint(0, 0, 0, prefix); } int sampleR = 0, sampleG = 0, sampleB = 0; for (int i = 0; i < n; i++) { sampleR += list[i].R; sampleG += list[i].G; sampleB += list[i].B; } ColorPoint cp = new(sampleR / n, sampleG / n, sampleB / n, prefix); //logger.LogInformation($"<{prefix}> HSB:({cp.Hue,3},{cp.Saturation:F3},{cp.Brightness:F3}), Gray:{cp.Gray,3}, RGB:({cp.R,3},{cp.G,3},{cp.B,3})"); return cp; } public Bitmap GrabImage22() { Bitmap bmp = null; if (VidPlayer2.InvokeRequired) { VidPlayer2.Invoke(new Action(() => bmp = VidPlayer2.GetCurrentVideoFrame())); } else { bmp = VidPlayer2.GetCurrentVideoFrame(); } return bmp; } } }