418 lines
13 KiB
C#
418 lines
13 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Timers;
|
|
using System.Net;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
|
|
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 string _configPath;
|
|
|
|
// 配置数据
|
|
private Rectangle _detectionZone;
|
|
private Rectangle _ledZone;
|
|
|
|
// 定时器
|
|
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 Color GetColor(int AreaNum)
|
|
{
|
|
switch (AreaNum)
|
|
{
|
|
case 1:
|
|
return Color.Red;
|
|
case 2:
|
|
return Color.Green;
|
|
case 3:
|
|
return Color.Blue;
|
|
default:
|
|
return Color.Gray;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前图像
|
|
/// </summary>
|
|
public Image GetCurrentImage()
|
|
{
|
|
lock (_imageLock)
|
|
{
|
|
return _currentImage;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 开始获取图像
|
|
/// </summary>
|
|
public void Start()
|
|
{
|
|
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();
|
|
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 Rectangle GetLedZone()
|
|
{
|
|
return _ledZone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置检测区域
|
|
/// </summary>
|
|
public void SetDetectionZone(Rectangle zone)
|
|
{
|
|
_detectionZone = zone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置Led区域
|
|
/// </summary>
|
|
public void SetLedZone(Rectangle zone)
|
|
{
|
|
_ledZone = zone;
|
|
}
|
|
|
|
/// <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)
|
|
{
|
|
writer.WriteLine(string.Format("{0},{1},{2},{3},#FF0000", _detectionZone.X, _detectionZone.Y, _detectionZone.Width, _detectionZone.Height));
|
|
}
|
|
|
|
writer.WriteLine();
|
|
|
|
writer.WriteLine("索引,X坐标,Y坐标,宽度,高度,颜色");
|
|
|
|
if (_ledZone.Width > 0 && _ledZone.Height > 0)
|
|
{
|
|
writer.WriteLine(string.Format("1,{0},{1},{2},{3},#00FF00", _ledZone.X, _ledZone.Y, _ledZone.Width, _ledZone.Height));
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// 加载配置
|
|
/// </summary>
|
|
private void LoadConfig()
|
|
{
|
|
if (string.IsNullOrEmpty(_configPath)) return;
|
|
|
|
_detectionZone = new Rectangle(0, 0, 2880, 1620);
|
|
_ledZone = new Rectangle(0, 0, 0, 0);
|
|
|
|
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 >= 4)
|
|
{
|
|
if (isDetectionZone)
|
|
{
|
|
int x = int.Parse(parts[0]);
|
|
int y = int.Parse(parts[1]);
|
|
int width = int.Parse(parts[2]);
|
|
int height = int.Parse(parts[3]);
|
|
_detectionZone = new Rectangle(x, y, width, height);
|
|
}
|
|
else if (parts[0] == "索引")
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
int index = int.Parse(parts[0]);
|
|
if (index == 1)
|
|
{
|
|
int x = int.Parse(parts[1]);
|
|
int y = int.Parse(parts[2]);
|
|
int width = int.Parse(parts[3]);
|
|
int height = int.Parse(parts[4]);
|
|
_ledZone = new Rectangle(x, y, width, height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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
|
|
{
|
|
Image image = GetCameraImage();
|
|
if (image != null)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("Camera: 捕获图像 Size=" + image.Width + "x" + image.Height);
|
|
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
|
|
{
|
|
if (_timer != null)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " 重新启动定时器");
|
|
_timer.Stop();
|
|
_timer.Start();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取摄像头图像
|
|
/// </summary>
|
|
private Image GetCameraImage()
|
|
{
|
|
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; }
|
|
}
|
|
} |