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;
}
}
}