| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- 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; }
- /// <summary>
- /// 当前画面,相机开启时有效
- /// </summary>
- public Bitmap CurrFrame
- {
- get => VidCapDevice2 is not null ? _currFrame.Clone() as Bitmap : null;
- protected set => _currFrame = value;
- }
- /// <summary>
- /// 摄像头是否已打开
- /// </summary>
- 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
- /// <summary>
- /// 画面刷新事件
- /// </summary>
- public event NewFrameEventHandler NewFrameEvent;
- #endregion
- #region 构造函数
- /// <summary>
- /// 初始化摄像头
- /// </summary>
- /// <param name="cameraMoniker">摄像头名称</param>
- public WebCamera2(string cameraMoniker)
- {
- VidCapDevice2 = new VideoCaptureDevice(cameraMoniker);
- VidCapDevice2.NewFrame += GetCurrFrame;
- VidPlayer2 = new VideoSourcePlayer
- {
- VideoSource = VidCapDevice2
- };
- _currFrame = null;
- }
- /// <summary>
- /// 初始化摄像头
- /// </summary>
- public WebCamera2()
- {
- VidCapDevice2 = new VideoCaptureDevice();
- VidPlayer2 = new VideoSourcePlayer
- {
- VideoSource = VidCapDevice2
- };
- _currFrame = null;
- }
- #endregion
- #region Camera Operate
- /// <summary>
- /// 连接指定的摄像头,并打开视频信号
- /// </summary>
- /// <param name="cameraMoniker"></param>
- /// <returns></returns>
- public bool OpenCamera(string cameraMoniker)
- {
- this.VidCapDevice2.Source = cameraMoniker;
- VidCapDevice2.NewFrame += this.GetCurrFrame;
- this.VidCapDevice2.Start();
- return this.VidCapDevice2.IsRunning;
- }
- /// <summary>
- /// 打开摄像头
- /// </summary>
- /// <returns></returns>
- 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;
- }
- /// <summary>
- /// 关闭摄像头
- /// </summary>
- 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();
- }
- }
- /// <summary>
- /// 拍照
- /// </summary>
- /// <returns></returns>
- 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
- /// <summary>
- /// 设置显示摄像头图像区域
- /// </summary>
- /// <param name="parentControl"></param>
- /// <param name="left"></param>
- /// <param name="top"></param>
- /// <param name="width"></param>
- /// <param name="height"></param>
- 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;
- }
- /// <summary>
- /// 显示摄像头属性设置窗体
- /// </summary>
- /// <param name="parentWindow"></param>
- public void ShowProperties(IntPtr parentWindow)
- {
- VidCapDevice2.DisplayPropertyPage(parentWindow);
- }
- /// <summary>
- /// 设置相机属性值
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="val"></param>
- /// <param name="flags"></param>
- public void SetProperties(CameraControlProperty prop, int val, CameraControlFlags flags)
- {
- VidCapDevice2.SetCameraProperty(prop, val, flags);// 取消自动曝光
- }
- /// <summary>
- /// 读取当前属性值
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="val"></param>
- /// <param name="flags"></param>
- public void GetProperties(CameraControlProperty prop, out int val, out CameraControlFlags flags)
- {
- VidCapDevice2.GetCameraProperty(prop, out val, out flags);
- }
- /// <summary>
- /// 读取相机参数的取值范围
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="minValue"></param>
- /// <param name="maxValue"></param>
- /// <param name="stepSize"></param>
- /// <param name="defaultValue"></param>
- /// <param name="flags"></param>
- 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);
- }
- /// <summary>
- /// 设置视频属性值,亮度、对比度、白平衡、增益等
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="val"></param>
- /// <param name="flags"></param>
- public void SetVideoProcAmpProperties(VideoProcAmpProperty prop, int val, VideoProcAmpFlags flags)
- {
- VidCapDevice2.SetVideoProcAmpProperty(prop, val, flags);
- }
- /// <summary>
- /// 读取当前视频参数
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="val"></param>
- /// <param name="flags"></param>
- public void GetVideoProcAmpProperties(VideoProcAmpProperty prop, out int val, out VideoProcAmpFlags flags)
- {
- VidCapDevice2.GetVideoProcAmpProperty(prop, out val, out flags);
- }
- /// <summary>
- /// 读取视频参数的取值范围
- /// </summary>
- /// <param name="prop"></param>
- /// <param name="minValue"></param>
- /// <param name="maxValue"></param>
- /// <param name="stepSize"></param>
- /// <param name="defaultValue"></param>
- /// <param name="flags"></param>
- 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);
- }
- /// <summary>
- /// 实时将新的画面存入CurrFrame对象中
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- public void GetCurrFrame(object sender, NewFrameEventArgs e)
- {
- try
- {
- if (e.Frame != null)
- {
- CurrFrame = e.Frame;
- }
- }
- catch (Exception)
- {
- // ignored
- }
- }
- #endregion
- #region 未使用
- /// <summary>
- /// 拍照,返回CurrFrame图像
- /// </summary>
- /// <returns></returns>
- //public Bitmap GrabImage2()
- //{
- // return CurrFrame;
- //}
- /// <summary>
- /// 返回视频支持的分辨率
- /// </summary>
- /// <returns></returns>
- public List<string> GetVideoResolution()
- {
- List<string> resolution = new List<string>();
- foreach (var cap in VidCapDevice2.VideoCapabilities)
- {
- resolution.Add(string.Format("{0} x {1}", cap.FrameSize.Width, cap.FrameSize.Height));
- }
- return resolution;
- }
- /// <summary>
- /// 设定视频的分辨率,index可以通过GetVideoResolution()获取所有支持的分辨率来确定
- /// </summary>
- /// <param name="index">在所有支持分辨率中的序号</param>
- 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<ColorPoint> cps = new List<ColorPoint>();
- 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<ColorPoint> cps = new List<ColorPoint>();
- 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<ColorPoint> 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;
- }
- }
- }
|