901 lines
31 KiB
C#
901 lines
31 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Drawing;
|
||
using System.Timers;
|
||
using System.Net;
|
||
using System.IO;
|
||
using System.Security.Cryptography;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading;
|
||
using Emgu.CV;
|
||
using Emgu.CV.CvEnum;
|
||
using Emgu.CV.Structure;
|
||
|
||
namespace Camera
|
||
{
|
||
public class Camera
|
||
{
|
||
// 摄像头配置信息
|
||
private const string IP = "192.168.100.60";
|
||
private const string SNAPSHOT_URI = "/snapshot.jpg";
|
||
private const string USER = "admin";
|
||
private const string PASSWD = "Yexian.net.168";
|
||
|
||
// 调试模式
|
||
private const Boolean IsDebug = false;
|
||
|
||
// 配置文件目录
|
||
private string _configPath;
|
||
|
||
// 配置数据
|
||
private Rectangle _detectionZone;
|
||
private ConcurrentDictionary<int, Rectangle> _ledZones = new ConcurrentDictionary<int, Rectangle>();
|
||
private ConcurrentDictionary<int, Color> _ledZoneColors = new ConcurrentDictionary<int, Color>();
|
||
private ConcurrentDictionary<int, string> _ledZoneDetectionResults = new ConcurrentDictionary<int, string>();
|
||
private ConcurrentDictionary<int, Tuple<double, double, double>> _ledZoneHsvValues = new ConcurrentDictionary<int, Tuple<double, double, double>>();
|
||
private ConcurrentDictionary<int, bool> _ledZoneVisibility = new ConcurrentDictionary<int, bool>();
|
||
private LedDetector _ledDetector = new LedDetector();
|
||
|
||
public void SetLedDetectorParams(int brightLimit, int satLimit)
|
||
{
|
||
_ledDetector.BrightLimit = brightLimit;
|
||
_ledDetector.SatLimit = satLimit;
|
||
}
|
||
|
||
public int GetBrightLimit()
|
||
{
|
||
return _ledDetector.BrightLimit;
|
||
}
|
||
|
||
public int GetSatLimit()
|
||
{
|
||
return _ledDetector.SatLimit;
|
||
}
|
||
|
||
public void SetColorThresholds(int redMin, int redMax, int greenMin, int greenMax, int blueMin, int blueMax)
|
||
{
|
||
_ledDetector.RedMin = redMin;
|
||
_ledDetector.RedMax = redMax;
|
||
_ledDetector.GreenMin = greenMin;
|
||
_ledDetector.GreenMax = greenMax;
|
||
_ledDetector.BlueMin = blueMin;
|
||
_ledDetector.BlueMax = blueMax;
|
||
}
|
||
|
||
public int GetRedMin() { return _ledDetector.RedMin; }
|
||
public int GetRedMax() { return _ledDetector.RedMax; }
|
||
public int GetGreenMin() { return _ledDetector.GreenMin; }
|
||
public int GetGreenMax() { return _ledDetector.GreenMax; }
|
||
public int GetBlueMin() { return _ledDetector.BlueMin; }
|
||
public int GetBlueMax() { return _ledDetector.BlueMax; }
|
||
|
||
private Color _detectionZoneColor = Color.Black;
|
||
|
||
// 检测状态
|
||
private bool _isDetecting = false;
|
||
private readonly object _detectionLock = new object();
|
||
|
||
// 定时器
|
||
private System.Timers.Timer _timer;
|
||
|
||
// 当前图像
|
||
private Image _currentImage;
|
||
private readonly object _imageLock = new object();
|
||
|
||
private string _authorization;
|
||
|
||
// 事件定义
|
||
public event EventHandler<ImageEventArgs> ImageCaptured;
|
||
|
||
/// <summary>
|
||
/// 根据区域编号获取检测结果
|
||
/// </summary>
|
||
/// <param name="AreaNum">区域编号</param>
|
||
/// <returns>检测结果字符串</returns>
|
||
public string GetColor(int AreaNum)
|
||
{
|
||
return GetLedZoneDetectionResult(AreaNum);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前图像(返回副本,避免线程冲突)
|
||
/// </summary>
|
||
public Image GetCurrentImage()
|
||
{
|
||
lock (_imageLock)
|
||
{
|
||
if (_currentImage != null)
|
||
return _currentImage.Clone() as Image;
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private static bool _isEmguCvAvailable = false;
|
||
private static string _emguCvError = "";
|
||
private static bool _isDllExtracted = false;
|
||
|
||
public static bool CheckEmguCv()
|
||
{
|
||
try
|
||
{
|
||
if (!_isDllExtracted)
|
||
{
|
||
ExtractDlls();
|
||
_isDllExtracted = true;
|
||
}
|
||
|
||
using (var testImage = new Image<Bgr, byte>(1, 1))
|
||
{
|
||
testImage.Dispose();
|
||
}
|
||
_isEmguCvAvailable = true;
|
||
return true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_isEmguCvAvailable = false;
|
||
_emguCvError = ex.Message;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static void ExtractDlls()
|
||
{
|
||
string appDir = AppDomain.CurrentDomain.BaseDirectory;
|
||
bool is64Bit = Environment.Is64BitProcess;
|
||
|
||
string[] dllFiles = is64Bit
|
||
? new string[] { "x64.cvextern.dll", "x64.opencv_ffmpeg310_64.dll" }
|
||
: new string[] { "x86.cvextern.dll", "x86.opencv_ffmpeg310.dll" };
|
||
|
||
string[] destFiles = is64Bit
|
||
? new string[] { "cvextern.dll", "opencv_ffmpeg310_64.dll" }
|
||
: new string[] { "cvextern.dll", "opencv_ffmpeg310.dll" };
|
||
|
||
for (int i = 0; i < dllFiles.Length; i++)
|
||
{
|
||
string destPath = Path.Combine(appDir, destFiles[i]);
|
||
if (!File.Exists(destPath))
|
||
{
|
||
try
|
||
{
|
||
using (var stream = typeof(Camera).Assembly.GetManifestResourceStream("Camera." + dllFiles[i]))
|
||
{
|
||
if (stream != null)
|
||
{
|
||
using (var fs = new FileStream(destPath, FileMode.Create))
|
||
{
|
||
byte[] buffer = new byte[4096];
|
||
int bytesRead;
|
||
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
|
||
{
|
||
fs.Write(buffer, 0, bytesRead);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
}
|
||
}
|
||
|
||
public static bool IsEmguCvAvailable()
|
||
{
|
||
return _isEmguCvAvailable;
|
||
}
|
||
|
||
public static string GetEmguCvError()
|
||
{
|
||
return _emguCvError;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始获取图像
|
||
/// </summary>
|
||
public void Start()
|
||
{
|
||
if (!CheckEmguCv())
|
||
{
|
||
throw new InvalidOperationException("Emgu CV 初始化失败:" + _emguCvError);
|
||
}
|
||
|
||
Stop();
|
||
|
||
_timer = new System.Timers.Timer(200);
|
||
_timer.AutoReset = false;
|
||
_timer.Elapsed += Timer_Elapsed;
|
||
_timer.Start();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止获取图像
|
||
/// </summary>
|
||
public void Stop()
|
||
{
|
||
if (_timer != null)
|
||
{
|
||
_timer.Stop();
|
||
_timer.Dispose();
|
||
_timer = null;
|
||
}
|
||
_authorization = null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开设置窗口
|
||
/// </summary>
|
||
public void SetArea()
|
||
{
|
||
Setting settingForm = new Setting();
|
||
settingForm.SetCamera(this);
|
||
if (_currentImage != null)
|
||
{
|
||
settingForm.SetImage(_currentImage);
|
||
}
|
||
if (!string.IsNullOrEmpty(_configPath))
|
||
{
|
||
settingForm.SetConfigPath(_configPath);
|
||
}
|
||
settingForm.ShowDialog();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置配置文件目录
|
||
/// </summary>
|
||
public void SetConfigPath(string path)
|
||
{
|
||
_configPath = path;
|
||
LoadConfig();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取检测区域
|
||
/// </summary>
|
||
public Rectangle GetDetectionZone()
|
||
{
|
||
return _detectionZone;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取Led区域列表
|
||
/// </summary>
|
||
public ConcurrentDictionary<int, Rectangle> GetLedZones()
|
||
{
|
||
return _ledZones;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定索引的Led区域
|
||
/// </summary>
|
||
public Rectangle GetLedZone(int index)
|
||
{
|
||
if (_ledZones.ContainsKey(index))
|
||
return _ledZones[index];
|
||
return new Rectangle(0, 0, 0, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取默认Led区域(索引为1)
|
||
/// </summary>
|
||
public Rectangle GetLedZone()
|
||
{
|
||
if (_ledZones.ContainsKey(1))
|
||
return _ledZones[1];
|
||
return new Rectangle(0, 0, 0, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置默认Led区域(索引为1)
|
||
/// </summary>
|
||
public void SetLedZone(Rectangle zone)
|
||
{
|
||
_ledZones[1] = zone;
|
||
if (!_ledZoneColors.ContainsKey(1))
|
||
{
|
||
_ledZoneColors[1] = Color.Lime;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取默认Led区域颜色(索引为1)
|
||
/// </summary>
|
||
public Color GetLedZoneColor()
|
||
{
|
||
if (_ledZoneColors.ContainsKey(1))
|
||
return _ledZoneColors[1];
|
||
return Color.Lime;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置默认Led区域颜色(索引为1)
|
||
/// </summary>
|
||
public void SetLedZoneColor(Color color)
|
||
{
|
||
_ledZoneColors[1] = color;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置检测区域
|
||
/// </summary>
|
||
public void SetDetectionZone(Rectangle zone)
|
||
{
|
||
_detectionZone = zone;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加Led区域
|
||
/// </summary>
|
||
public void AddLedZone(int index, Rectangle zone, Color color)
|
||
{
|
||
// 裁截LED区域,确保在图像范围内
|
||
zone = ClipZone(zone);
|
||
_ledZones[index] = zone;
|
||
_ledZoneColors[index] = color;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置指定索引的Led区域
|
||
/// </summary>
|
||
public void SetLedZone(int index, Rectangle zone)
|
||
{
|
||
if (_ledZones.ContainsKey(index))
|
||
{
|
||
// 裁截LED区域,确保在图像范围内
|
||
zone = ClipZone(zone);
|
||
_ledZones[index] = zone;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 裁截区域,确保在图像范围内
|
||
/// </summary>
|
||
private Rectangle ClipZone(Rectangle zone)
|
||
{
|
||
if (_currentImage == null) return zone;
|
||
|
||
int imageWidth = _currentImage.Width;
|
||
int imageHeight = _currentImage.Height;
|
||
|
||
// 确保坐标和尺寸在图像范围内
|
||
int x = Math.Max(0, zone.X);
|
||
int y = Math.Max(0, zone.Y);
|
||
int width = Math.Min(zone.Width, imageWidth - x);
|
||
int height = Math.Min(zone.Height, imageHeight - y);
|
||
|
||
// 确保宽度和高度为正数
|
||
width = Math.Max(1, width);
|
||
height = Math.Max(1, height);
|
||
|
||
return new Rectangle(x, y, width, height);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除Led区域
|
||
/// </summary>
|
||
public void RemoveLedZone(int index)
|
||
{
|
||
Rectangle r;
|
||
Color c;
|
||
string s;
|
||
_ledZones.TryRemove(index, out r);
|
||
_ledZoneColors.TryRemove(index, out c);
|
||
_ledZoneDetectionResults.TryRemove(index, out s);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置指定索引的Led区域颜色
|
||
/// </summary>
|
||
public void SetLedZoneColor(int index, Color color)
|
||
{
|
||
if (_ledZoneColors.ContainsKey(index))
|
||
{
|
||
_ledZoneColors[index] = color;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取Led区域数量
|
||
/// </summary>
|
||
public int GetLedZoneCount()
|
||
{
|
||
return _ledZones.Count;
|
||
}
|
||
|
||
public Color GetDetectionZoneColor()
|
||
{
|
||
return _detectionZoneColor;
|
||
}
|
||
|
||
public ConcurrentDictionary<int, Color> GetLedZoneColors()
|
||
{
|
||
return _ledZoneColors;
|
||
}
|
||
|
||
public void SetDetectionZoneColor(Color color)
|
||
{
|
||
_detectionZoneColor = color;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定索引的Led区域颜色
|
||
/// </summary>
|
||
public Color GetLedZoneColor(int index)
|
||
{
|
||
if (_ledZoneColors.ContainsKey(index))
|
||
return _ledZoneColors[index];
|
||
return Color.Lime;
|
||
}
|
||
|
||
public bool GetLedZoneVisibility(int index)
|
||
{
|
||
if (_ledZoneVisibility.ContainsKey(index))
|
||
return _ledZoneVisibility[index];
|
||
return true;
|
||
}
|
||
|
||
public void SetLedZoneVisibility(int index, bool visible)
|
||
{
|
||
_ledZoneVisibility[index] = visible;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取LED区域检测结果
|
||
/// </summary>
|
||
public ConcurrentDictionary<int, string> GetLedZoneDetectionResults()
|
||
{
|
||
return _ledZoneDetectionResults;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定索引的LED区域检测结果
|
||
/// </summary>
|
||
public string GetLedZoneDetectionResult(int index)
|
||
{
|
||
if (_ledZoneDetectionResults.ContainsKey(index))
|
||
return _ledZoneDetectionResults[index];
|
||
return "";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置LED区域检测结果
|
||
/// </summary>
|
||
public void SetLedZoneDetectionResult(int index, string result)
|
||
{
|
||
_ledZoneDetectionResults[index] = result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有LED区域的HSV值
|
||
/// </summary>
|
||
public ConcurrentDictionary<int, Tuple<double, double, double>> GetLedZoneHsvValues()
|
||
{
|
||
return _ledZoneHsvValues;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定索引的LED区域的HSV值
|
||
/// </summary>
|
||
public Tuple<double, double, double> GetLedZoneHsvValue(int index)
|
||
{
|
||
if (_ledZoneHsvValues.ContainsKey(index))
|
||
return _ledZoneHsvValues[index];
|
||
return new Tuple<double, double, double>(0, 0, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存配置
|
||
/// </summary>
|
||
public void SaveConfig()
|
||
{
|
||
if (string.IsNullOrEmpty(_configPath)) return;
|
||
|
||
string configFile = Path.Combine(_configPath, "Camera.csv");
|
||
try
|
||
{
|
||
using (StreamWriter writer = new StreamWriter(configFile, false, System.Text.Encoding.UTF8))
|
||
{
|
||
writer.WriteLine("X坐标,Y坐标,宽度,高度,颜色");
|
||
|
||
if (_detectionZone.Width > 0 && _detectionZone.Height > 0)
|
||
{
|
||
string colorStr = ColorTranslator.ToHtml(_detectionZoneColor);
|
||
writer.WriteLine(string.Format("{0},{1},{2},{3},{4}", _detectionZone.X, _detectionZone.Y, _detectionZone.Width, _detectionZone.Height, colorStr));
|
||
}
|
||
|
||
writer.WriteLine();
|
||
|
||
writer.WriteLine("索引,X坐标,Y坐标,宽度,高度,颜色");
|
||
|
||
foreach (var kvp in _ledZones)
|
||
{
|
||
int index = kvp.Key;
|
||
Rectangle ledZone = kvp.Value;
|
||
Color ledColor = _ledZoneColors[index];
|
||
string colorStr = ColorTranslator.ToHtml(ledColor);
|
||
writer.WriteLine(string.Format("{0},{1},{2},{3},{4},{5}", index, ledZone.X, ledZone.Y, ledZone.Width, ledZone.Height, colorStr));
|
||
}
|
||
|
||
writer.WriteLine();
|
||
writer.WriteLine(string.Format("亮灭阈值,{0},{1}", _ledDetector.BrightLimit, _ledDetector.SatLimit));
|
||
writer.WriteLine(string.Format("红阈值,{0},{1}", _ledDetector.RedMin, _ledDetector.RedMax));
|
||
writer.WriteLine(string.Format("绿阈值,{0},{1}", _ledDetector.GreenMin, _ledDetector.GreenMax));
|
||
writer.WriteLine(string.Format("蓝阈值,{0},{1}", _ledDetector.BlueMin, _ledDetector.BlueMax));
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载配置
|
||
/// </summary>
|
||
private void LoadConfig()
|
||
{
|
||
if (string.IsNullOrEmpty(_configPath)) return;
|
||
|
||
_detectionZone = new Rectangle(0, 0, 2880, 1620);
|
||
_ledZones.Clear();
|
||
_ledZoneColors.Clear();
|
||
_detectionZoneColor = Color.Black;
|
||
|
||
string configFile = Path.Combine(_configPath, "Camera.csv");
|
||
try
|
||
{
|
||
if (File.Exists(configFile))
|
||
{
|
||
using (StreamReader reader = new StreamReader(configFile, System.Text.Encoding.UTF8))
|
||
{
|
||
string line;
|
||
bool isDetectionZone = true;
|
||
while ((line = reader.ReadLine()) != null)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(line))
|
||
{
|
||
isDetectionZone = false;
|
||
continue;
|
||
}
|
||
|
||
string[] parts = line.Split(',');
|
||
if (parts.Length >= 2 && parts[0] == "亮灭阈值")
|
||
{
|
||
if (int.TryParse(parts[1], out int brightLimit))
|
||
_ledDetector.BrightLimit = brightLimit;
|
||
if (parts.Length >= 3 && int.TryParse(parts[2], out int satLimit))
|
||
_ledDetector.SatLimit = satLimit;
|
||
continue;
|
||
}
|
||
|
||
if (parts.Length >= 3 && parts[0] == "红阈值")
|
||
{
|
||
if (int.TryParse(parts[1], out int redMin))
|
||
_ledDetector.RedMin = redMin;
|
||
if (int.TryParse(parts[2], out int redMax))
|
||
_ledDetector.RedMax = redMax;
|
||
continue;
|
||
}
|
||
|
||
if (parts.Length >= 3 && parts[0] == "绿阈值")
|
||
{
|
||
if (int.TryParse(parts[1], out int greenMin))
|
||
_ledDetector.GreenMin = greenMin;
|
||
if (int.TryParse(parts[2], out int greenMax))
|
||
_ledDetector.GreenMax = greenMax;
|
||
continue;
|
||
}
|
||
|
||
if (parts.Length >= 3 && parts[0] == "蓝阈值")
|
||
{
|
||
if (int.TryParse(parts[1], out int blueMin))
|
||
_ledDetector.BlueMin = blueMin;
|
||
if (int.TryParse(parts[2], out int blueMax))
|
||
_ledDetector.BlueMax = blueMax;
|
||
continue;
|
||
}
|
||
|
||
if (parts.Length >= 4)
|
||
{
|
||
if (isDetectionZone)
|
||
{
|
||
if (!int.TryParse(parts[0], out int x)) continue;
|
||
if (!int.TryParse(parts[1], out int y)) continue;
|
||
if (!int.TryParse(parts[2], out int width)) continue;
|
||
if (!int.TryParse(parts[3], out int height)) continue;
|
||
_detectionZone = new Rectangle(x, y, width, height);
|
||
if (parts.Length >= 5 && !string.IsNullOrEmpty(parts[4]))
|
||
{
|
||
try { _detectionZoneColor = ColorTranslator.FromHtml(parts[4]); } catch { }
|
||
}
|
||
}
|
||
else if (parts[0] == "索引")
|
||
{
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
if (!int.TryParse(parts[0], out int index)) continue;
|
||
if (parts.Length >= 6)
|
||
{
|
||
if (!int.TryParse(parts[1], out int x)) continue;
|
||
if (!int.TryParse(parts[2], out int y)) continue;
|
||
if (!int.TryParse(parts[3], out int width)) continue;
|
||
if (!int.TryParse(parts[4], out int height)) continue;
|
||
Color zoneColor = Color.Lime;
|
||
if (parts.Length >= 6 && !string.IsNullOrEmpty(parts[5]))
|
||
{
|
||
try { zoneColor = ColorTranslator.FromHtml(parts[5]); } catch { }
|
||
}
|
||
_ledZones[index] = new Rectangle(x, y, width, height);
|
||
_ledZoneColors[index] = zoneColor;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SaveConfig();
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时器触发事件
|
||
/// </summary>
|
||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " Timer_Elapsed 开始");
|
||
try
|
||
{
|
||
lock (_detectionLock)
|
||
{
|
||
if (_isDetecting)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("Camera: 正在检测,跳过本次");
|
||
if (_timer != null)
|
||
{
|
||
_timer.Stop();
|
||
_timer.Start();
|
||
}
|
||
return;
|
||
}
|
||
_isDetecting = true;
|
||
}
|
||
|
||
Image image = GetCameraImage();
|
||
if (image != null)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("Camera: 捕获图像 Size=" + image.Width + "x" + image.Height);
|
||
|
||
DetectLedZones(image);
|
||
|
||
Image oldImage = null;
|
||
lock (_imageLock)
|
||
{
|
||
oldImage = _currentImage;
|
||
_currentImage = image;
|
||
}
|
||
if (oldImage != null)
|
||
{
|
||
oldImage.Dispose();
|
||
}
|
||
OnImageCaptured(new ImageEventArgs { Image = image });
|
||
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " 事件已触发");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " 获取图像失败: " + ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
lock (_detectionLock)
|
||
{
|
||
_isDetecting = false;
|
||
}
|
||
if (_timer != null)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " 重新启动定时器");
|
||
_timer.Stop();
|
||
_timer.Start();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测LED区域颜色
|
||
/// </summary>
|
||
private void DetectLedZones(Image image)
|
||
{
|
||
if (image == null || _ledZones.Count == 0) return;
|
||
if (_detectionZone.Width <= 0 || _detectionZone.Height <= 0) return;
|
||
|
||
try
|
||
{
|
||
using (Bitmap bmp = new Bitmap(image))
|
||
{
|
||
if (_detectionZone.X < 0 || _detectionZone.Y < 0 ||
|
||
_detectionZone.X + _detectionZone.Width > bmp.Width ||
|
||
_detectionZone.Y + _detectionZone.Height > bmp.Height)
|
||
return;
|
||
|
||
using (var imageBgr = new Image<Bgr, byte>(bmp))
|
||
{
|
||
foreach (var kvp in _ledZones)
|
||
{
|
||
int index = kvp.Key;
|
||
Rectangle zone = kvp.Value;
|
||
|
||
if (zone.Width <= 0 || zone.Height <= 0) continue;
|
||
Rectangle ledRect = new Rectangle(
|
||
_detectionZone.X + zone.X,
|
||
_detectionZone.Y + zone.Y,
|
||
zone.Width,
|
||
zone.Height);
|
||
|
||
if (ledRect.X < 0 || ledRect.Y < 0 ||
|
||
ledRect.X + ledRect.Width > bmp.Width ||
|
||
ledRect.Y + ledRect.Height > bmp.Height)
|
||
continue;
|
||
|
||
var detectResult = _ledDetector.Detect(imageBgr, ledRect);
|
||
string result = "";
|
||
if (detectResult.Item1 == LedState.On)
|
||
{
|
||
if (detectResult.Item2 == LedColor.Red)
|
||
result = "Red";
|
||
else if (detectResult.Item2 == LedColor.Green)
|
||
result = "Green";
|
||
else if (detectResult.Item2 == LedColor.Blue)
|
||
result = "Blue";
|
||
}
|
||
else
|
||
{
|
||
result = "Off";
|
||
}
|
||
_ledZoneDetectionResults[index] = result;
|
||
_ledZoneHsvValues[index] = new Tuple<double, double, double>(detectResult.Item3, detectResult.Item4, detectResult.Item5);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("Camera: 检测LED区域失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取摄像头图像
|
||
/// </summary>
|
||
private Image GetCameraImage()
|
||
{
|
||
#pragma warning disable 0162
|
||
if (IsDebug)
|
||
{
|
||
if (File.Exists("test.jpg"))
|
||
return new Bitmap("test.jpg");
|
||
}
|
||
#pragma warning restore 0162
|
||
string url = "http://" + IP + SNAPSHOT_URI;
|
||
|
||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||
request.Method = "GET";
|
||
request.Headers.Add("Cache-Control", "no-cache");
|
||
request.Headers.Add("Pragma", "no-cache");
|
||
|
||
if (!string.IsNullOrEmpty(_authorization))
|
||
{
|
||
request.Headers.Add("Authorization", _authorization);
|
||
}
|
||
|
||
HttpWebResponse response = null;
|
||
try
|
||
{
|
||
response = (HttpWebResponse)request.GetResponse();
|
||
}
|
||
catch (System.Net.WebException ex)
|
||
{
|
||
if (ex.Response != null && ((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.Unauthorized)
|
||
{
|
||
response = (HttpWebResponse)ex.Response;
|
||
string authHeader = response.Headers["WWW-Authenticate"];
|
||
response.Close();
|
||
|
||
string realm = ExtractAuthParam(authHeader, "realm");
|
||
string nonce = ExtractAuthParam(authHeader, "nonce");
|
||
|
||
_authorization = GenerateDigestAuthorization(USER, PASSWD, realm, nonce, "GET", SNAPSHOT_URI);
|
||
|
||
request = (HttpWebRequest)WebRequest.Create(url);
|
||
request.Method = "GET";
|
||
request.Headers.Add("Authorization", _authorization);
|
||
request.Headers.Add("Cache-Control", "no-cache");
|
||
request.Headers.Add("Pragma", "no-cache");
|
||
response = (HttpWebResponse)request.GetResponse();
|
||
}
|
||
else
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
byte[] imageData;
|
||
using (Stream stream = response.GetResponseStream())
|
||
using (MemoryStream memoryStream = new MemoryStream())
|
||
{
|
||
stream.CopyTo(memoryStream);
|
||
imageData = memoryStream.ToArray();
|
||
}
|
||
|
||
return new Bitmap(new MemoryStream(imageData));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从WWW-Authenticate头中提取参数
|
||
/// </summary>
|
||
private string ExtractAuthParam(string authHeader, string paramName)
|
||
{
|
||
Regex regex = new Regex(paramName + "=\"([^\"]+)\"");
|
||
Match match = regex.Match(authHeader);
|
||
if (match.Success)
|
||
{
|
||
return match.Groups[1].Value;
|
||
}
|
||
return string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成Digest认证的Authorization头
|
||
/// </summary>
|
||
private string GenerateDigestAuthorization(string username, string password, string realm, string nonce, string method, string uri)
|
||
{
|
||
string ha1 = CalculateMD5(username + ":" + realm + ":" + password);
|
||
string ha2 = CalculateMD5(method + ":" + uri);
|
||
string response = CalculateMD5(ha1 + ":" + nonce + ":" + ha2);
|
||
|
||
return string.Format("Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"",
|
||
username, realm, nonce, uri, response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算MD5哈希值
|
||
/// </summary>
|
||
private string CalculateMD5(string input)
|
||
{
|
||
using (MD5 md5 = MD5.Create())
|
||
{
|
||
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
|
||
byte[] hashBytes = md5.ComputeHash(inputBytes);
|
||
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
for (int i = 0; i < hashBytes.Length; i++)
|
||
{
|
||
sb.Append(hashBytes[i].ToString("x2"));
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发图像捕获事件
|
||
/// </summary>
|
||
protected virtual void OnImageCaptured(ImageEventArgs e)
|
||
{
|
||
if (ImageCaptured != null)
|
||
{
|
||
ImageCaptured(this, e);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 图像事件参数
|
||
/// </summary>
|
||
public class ImageEventArgs : EventArgs
|
||
{
|
||
public Image Image { get; set; }
|
||
}
|
||
} |