2026-01-05 19:21:33 +08:00
|
|
|
|
using MvCamCtrl.NET;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Drawing.Imaging;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using System.Windows.Forms;
|
2026-01-06 10:34:48 +08:00
|
|
|
|
using Accord.Video;
|
|
|
|
|
|
using Accord.Video.FFMPEG;
|
2026-01-05 19:21:33 +08:00
|
|
|
|
|
|
|
|
|
|
namespace BasicDemo
|
|
|
|
|
|
{
|
|
|
|
|
|
public partial class AutoCameraForm : Form
|
|
|
|
|
|
{
|
|
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
|
|
|
|
|
|
private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
|
|
|
|
|
|
|
|
|
|
|
// 支持最多3个相机
|
|
|
|
|
|
private const int MAX_CAMERAS = 3;
|
|
|
|
|
|
|
|
|
|
|
|
// 相机相关对象数组
|
|
|
|
|
|
private MyCamera[] cameras = new MyCamera[MAX_CAMERAS];
|
|
|
|
|
|
private bool[] grabbingFlags = new bool[MAX_CAMERAS];
|
|
|
|
|
|
private Thread[] grabThreads = new Thread[MAX_CAMERAS];
|
|
|
|
|
|
private MyCamera.MV_FRAME_OUT_INFO_EX[] frameInfos = new MyCamera.MV_FRAME_OUT_INFO_EX[MAX_CAMERAS];
|
|
|
|
|
|
|
|
|
|
|
|
// 图像显示相关
|
|
|
|
|
|
private Bitmap[] displayBitmaps = new Bitmap[MAX_CAMERAS];
|
|
|
|
|
|
private IntPtr[] displayBufs = new IntPtr[MAX_CAMERAS];
|
|
|
|
|
|
private UInt32[] displayBufSizes = new UInt32[MAX_CAMERAS];
|
|
|
|
|
|
|
|
|
|
|
|
// 设备列表
|
|
|
|
|
|
private MyCamera.MV_CC_DEVICE_INFO_LIST deviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
|
|
|
|
|
|
|
|
|
|
|
|
// UI控件
|
|
|
|
|
|
private PictureBox[] pictureBoxes = new PictureBox[MAX_CAMERAS];
|
|
|
|
|
|
private Label[] statusLabels = new Label[MAX_CAMERAS];
|
|
|
|
|
|
|
|
|
|
|
|
// 锁对象,用于线程同步
|
|
|
|
|
|
private static readonly object[] locks = new object[MAX_CAMERAS] { new object(), new object(), new object() };
|
|
|
|
|
|
|
|
|
|
|
|
// 状态回调
|
|
|
|
|
|
public delegate bool GetRunStatusDelegate();
|
|
|
|
|
|
private GetRunStatusDelegate _getRunStatusCallback;
|
|
|
|
|
|
private Thread _statusCheckThread;
|
|
|
|
|
|
|
|
|
|
|
|
// 添加:定义返回事件委托
|
|
|
|
|
|
public delegate void ReturnToPreviousFormDelegate();
|
|
|
|
|
|
public event ReturnToPreviousFormDelegate OnReturnRequested;
|
|
|
|
|
|
|
|
|
|
|
|
// 控制变量
|
|
|
|
|
|
private bool _shouldCapture = false;
|
|
|
|
|
|
private bool _camerasInitialized = false;
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 视频保存配置(新增)
|
|
|
|
|
|
private string _videoSavePath = @"D:\CameraRecordings\"; // 修改为你的保存路径
|
|
|
|
|
|
private double _frameRate = 30.0; // 默认帧率
|
|
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
// 构造函数
|
2026-01-06 10:34:48 +08:00
|
|
|
|
public AutoCameraForm(GetRunStatusDelegate getRunStatusCallback, ReturnToPreviousFormDelegate returnCallback = null, string videoSavePath = null)
|
2026-01-05 19:21:33 +08:00
|
|
|
|
{
|
|
|
|
|
|
_getRunStatusCallback = getRunStatusCallback;
|
|
|
|
|
|
// 如果提供了返回回调,就订阅
|
|
|
|
|
|
if (returnCallback != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
OnReturnRequested += returnCallback;
|
|
|
|
|
|
}
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-06 13:34:31 +08:00
|
|
|
|
//// 设置视频保存路径
|
|
|
|
|
|
//if (!string.IsNullOrEmpty(videoSavePath))
|
|
|
|
|
|
//{
|
|
|
|
|
|
// _videoSavePath = videoSavePath;
|
|
|
|
|
|
//}
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-06 13:34:31 +08:00
|
|
|
|
//// 确保保存目录存在
|
|
|
|
|
|
//Directory.CreateDirectory(_videoSavePath);
|
2026-01-05 19:21:33 +08:00
|
|
|
|
InitializeComponent();
|
|
|
|
|
|
Control.CheckForIllegalCrossThreadCalls = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 设置窗体大小
|
|
|
|
|
|
this.Size = new Size(1900, 1000);
|
|
|
|
|
|
this.Text = "三相机自动监控系统";
|
|
|
|
|
|
this.StartPosition = FormStartPosition.CenterScreen;
|
|
|
|
|
|
this.FormClosing += AutoCameraForm_FormClosing;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化UI
|
|
|
|
|
|
InitializeUI();
|
|
|
|
|
|
|
|
|
|
|
|
// 窗体加载时初始化相机
|
|
|
|
|
|
this.Load += (s, e) => InitializeCameras();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化UI界面
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeUI()
|
|
|
|
|
|
{
|
|
|
|
|
|
this.BackColor = Color.FromArgb(240, 240, 240);
|
|
|
|
|
|
//Button btnDiagnose = new Button
|
|
|
|
|
|
//{
|
|
|
|
|
|
// Text = "诊断",
|
|
|
|
|
|
// Size = new Size(80, 30),
|
|
|
|
|
|
// Location = new Point(1800, 30),
|
|
|
|
|
|
// BackColor = Color.Yellow,
|
|
|
|
|
|
// ForeColor = Color.Black
|
|
|
|
|
|
//};
|
|
|
|
|
|
//btnDiagnose.Click += (s, e) => DiagnoseCameras();
|
|
|
|
|
|
//this.Controls.Add(btnDiagnose);
|
|
|
|
|
|
// 创建标题
|
|
|
|
|
|
Label titleLabel = new Label
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "三相机自动监控系统",
|
|
|
|
|
|
Font = new Font("Microsoft YaHei", 20, FontStyle.Bold),
|
|
|
|
|
|
ForeColor = Color.FromArgb(0, 112, 192),
|
|
|
|
|
|
Size = new Size(500, 40),
|
|
|
|
|
|
Location = new Point(700, 20),
|
|
|
|
|
|
TextAlign = ContentAlignment.MiddleCenter
|
|
|
|
|
|
};
|
|
|
|
|
|
this.Controls.Add(titleLabel);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建状态栏
|
|
|
|
|
|
Panel statusBar = new Panel
|
|
|
|
|
|
{
|
|
|
|
|
|
BackColor = Color.FromArgb(0, 112, 192),
|
|
|
|
|
|
Size = new Size(1900, 30),
|
|
|
|
|
|
Location = new Point(0, 970)
|
|
|
|
|
|
};
|
|
|
|
|
|
this.Controls.Add(statusBar);
|
|
|
|
|
|
|
|
|
|
|
|
Label statusText = new Label
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "系统状态:正在初始化...",
|
|
|
|
|
|
Font = new Font("Microsoft YaHei", 10),
|
|
|
|
|
|
ForeColor = Color.White,
|
|
|
|
|
|
Size = new Size(300, 25),
|
|
|
|
|
|
Location = new Point(10, 2),
|
|
|
|
|
|
TextAlign = ContentAlignment.MiddleLeft
|
|
|
|
|
|
};
|
|
|
|
|
|
statusBar.Controls.Add(statusText);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建三个相机显示区域
|
|
|
|
|
|
int pictureWidth = 620;
|
|
|
|
|
|
int pictureHeight = 850;
|
|
|
|
|
|
int margin = 20;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CAMERAS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 创建容器面板
|
|
|
|
|
|
Panel cameraPanel = new Panel
|
|
|
|
|
|
{
|
|
|
|
|
|
BackColor = Color.White,
|
|
|
|
|
|
BorderStyle = BorderStyle.FixedSingle,
|
|
|
|
|
|
Size = new Size(pictureWidth, 900),
|
|
|
|
|
|
Location = new Point(margin + i * (pictureWidth + margin), 80)
|
|
|
|
|
|
};
|
|
|
|
|
|
this.Controls.Add(cameraPanel);
|
|
|
|
|
|
|
|
|
|
|
|
// 相机标题
|
|
|
|
|
|
Label cameraTitle = new Label
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = $"相机 {i + 1}",
|
|
|
|
|
|
Font = new Font("Microsoft YaHei", 14, FontStyle.Bold),
|
|
|
|
|
|
ForeColor = Color.FromArgb(0, 112, 192),
|
|
|
|
|
|
Size = new Size(200, 30),
|
|
|
|
|
|
Location = new Point(10, 10),
|
|
|
|
|
|
TextAlign = ContentAlignment.MiddleLeft
|
|
|
|
|
|
};
|
|
|
|
|
|
cameraPanel.Controls.Add(cameraTitle);
|
|
|
|
|
|
|
|
|
|
|
|
// 状态标签
|
|
|
|
|
|
statusLabels[i] = new Label
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "状态:未连接",
|
|
|
|
|
|
Font = new Font("Microsoft YaHei", 10),
|
|
|
|
|
|
ForeColor = Color.Red,
|
|
|
|
|
|
Size = new Size(200, 25),
|
|
|
|
|
|
Location = new Point(10, 45),
|
|
|
|
|
|
TextAlign = ContentAlignment.MiddleLeft
|
|
|
|
|
|
};
|
|
|
|
|
|
cameraPanel.Controls.Add(statusLabels[i]);
|
|
|
|
|
|
|
|
|
|
|
|
// 图像显示区域
|
|
|
|
|
|
pictureBoxes[i] = new PictureBox
|
|
|
|
|
|
{
|
|
|
|
|
|
Size = new Size(pictureWidth - 40, pictureHeight),
|
|
|
|
|
|
Location = new Point(20, 80),
|
|
|
|
|
|
BackColor = Color.Black,
|
|
|
|
|
|
SizeMode = PictureBoxSizeMode.StretchImage,
|
|
|
|
|
|
BorderStyle = BorderStyle.FixedSingle
|
|
|
|
|
|
};
|
|
|
|
|
|
cameraPanel.Controls.Add(pictureBoxes[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AddBackButton();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddBackButton()
|
|
|
|
|
|
{
|
|
|
|
|
|
Button btnBack = new Button
|
|
|
|
|
|
{
|
|
|
|
|
|
Text = "返回",
|
|
|
|
|
|
Size = new Size(100, 40),
|
|
|
|
|
|
Location = new Point(1750, 30),
|
|
|
|
|
|
Font = new Font("Microsoft YaHei", 12),
|
|
|
|
|
|
BackColor = Color.FromArgb(0, 112, 192),
|
|
|
|
|
|
ForeColor = Color.White,
|
|
|
|
|
|
FlatStyle = FlatStyle.Flat
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
btnBack.Click += (s, e) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 触发返回事件
|
|
|
|
|
|
OnReturnRequested?.Invoke();
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭当前窗体
|
|
|
|
|
|
this.Close();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.Controls.Add(btnBack);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化相机系统
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeCameras()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateSystemStatus("正在初始化SDK...");
|
|
|
|
|
|
|
|
|
|
|
|
// 等待窗体完全加载
|
|
|
|
|
|
Application.DoEvents();
|
|
|
|
|
|
Thread.Sleep(500);
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化SDK
|
|
|
|
|
|
int ret = MyCamera.MV_CC_Initialize_NET();
|
|
|
|
|
|
if (ret != MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"SDK初始化失败,错误代码: {ret}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus("正在枚举设备...");
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 枚举设备
|
|
|
|
|
|
ret = MyCamera.MV_CC_EnumDevices_NET(
|
|
|
|
|
|
MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE,
|
|
|
|
|
|
ref deviceList);
|
|
|
|
|
|
|
|
|
|
|
|
if (ret != MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"设备枚举失败,错误代码: {ret}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 检查设备数量
|
|
|
|
|
|
if (deviceList.nDeviceNum < MAX_CAMERAS)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"检测到 {deviceList.nDeviceNum} 个设备,需要至少 {MAX_CAMERAS} 个相机",
|
|
|
|
|
|
"错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus($"检测到 {deviceList.nDeviceNum} 个设备,准备初始化前 {MAX_CAMERAS} 个相机...");
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 逐个初始化相机 - 增加间隔时间
|
|
|
|
|
|
for (int i = 0; i < MAX_CAMERAS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (InitializeSingleCamera(i))
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(i, "设备已就绪", Color.Blue);
|
|
|
|
|
|
}
|
|
|
|
|
|
Thread.Sleep(1000); // 增加间隔,避免同时打开冲突
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_camerasInitialized = true;
|
|
|
|
|
|
UpdateSystemStatus("所有相机已初始化就绪,等待测试开始...");
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 启动状态检查线程 - 延迟启动
|
|
|
|
|
|
Thread.Sleep(1000);
|
|
|
|
|
|
StartStatusCheckThread();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"初始化系统时发生异常: {ex.Message}", "异常", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 启动状态检查线程
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void StartStatusCheckThread()
|
|
|
|
|
|
{
|
|
|
|
|
|
_statusCheckThread = new Thread(CheckRunStatusLoop)
|
|
|
|
|
|
{
|
|
|
|
|
|
IsBackground = true
|
|
|
|
|
|
};
|
|
|
|
|
|
_statusCheckThread.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 状态检查循环 - 添加异常处理
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CheckRunStatusLoop()
|
|
|
|
|
|
{
|
|
|
|
|
|
bool lastStatus = false;
|
|
|
|
|
|
|
|
|
|
|
|
while (!this.IsDisposed && _camerasInitialized)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
bool currentStatus = _getRunStatusCallback?.Invoke() ?? false;
|
|
|
|
|
|
|
|
|
|
|
|
if (currentStatus != lastStatus)
|
|
|
|
|
|
{
|
|
|
|
|
|
lastStatus = currentStatus;
|
|
|
|
|
|
|
|
|
|
|
|
// 使用TryInvoke避免窗体已关闭时的异常
|
|
|
|
|
|
if (!this.IsDisposed && this.IsHandleCreated)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.BeginInvoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (currentStatus)
|
|
|
|
|
|
{
|
|
|
|
|
|
StartAllCamerasCapture();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
StopAllCamerasCapture();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"状态切换异常: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Thread.Sleep(1000); // 延长检查间隔,减少CPU使用
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
// 忽略异常
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 开始所有相机采集
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void StartAllCamerasCapture()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_camerasInitialized) return;
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus("测试开始,正在启动相机采集...");
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CAMERAS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cameras[i] != null && !grabbingFlags[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
StartSingleCameraCapture(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus("所有相机已开始采集");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 停止所有相机采集
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void StopAllCamerasCapture()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_camerasInitialized) return;
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus("测试结束,正在停止相机采集...");
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_CAMERAS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cameras[i] != null && grabbingFlags[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
StopSingleCameraCapture(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateSystemStatus("所有相机已停止采集");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化单个相机
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool InitializeSingleCamera(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, "正在打开...", Color.Orange);
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 创建设备信息
|
|
|
|
|
|
MyCamera.MV_CC_DEVICE_INFO device =
|
|
|
|
|
|
(MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(
|
|
|
|
|
|
deviceList.pDeviceInfo[cameraIndex], typeof(MyCamera.MV_CC_DEVICE_INFO));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 创建相机对象
|
|
|
|
|
|
cameras[cameraIndex] = new MyCamera();
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 创建设备
|
|
|
|
|
|
int ret = cameras[cameraIndex].MV_CC_CreateDevice_NET(ref device);
|
|
|
|
|
|
if (ret != MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"创建设备失败: {ret}", Color.Red);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 打开设备
|
|
|
|
|
|
ret = cameras[cameraIndex].MV_CC_OpenDevice_NET();
|
|
|
|
|
|
if (ret != MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
cameras[cameraIndex].MV_CC_DestroyDevice_NET();
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"打开设备失败: {ret}", Color.Red);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 如果是千兆网相机,设置最优包大小
|
|
|
|
|
|
if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
|
|
|
|
|
|
{
|
|
|
|
|
|
int packetSize = cameras[cameraIndex].MV_CC_GetOptimalPacketSize_NET();
|
|
|
|
|
|
if (packetSize > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
cameras[cameraIndex].MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", packetSize);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 设置采集模式
|
|
|
|
|
|
cameras[cameraIndex].MV_CC_SetEnumValue_NET("AcquisitionMode",
|
|
|
|
|
|
(uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
|
|
|
|
|
|
cameras[cameraIndex].MV_CC_SetEnumValue_NET("TriggerMode",
|
|
|
|
|
|
(uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_OFF);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"异常: {ex.Message}", Color.Red);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void StartSingleCameraCapture(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cameras[cameraIndex] == null || grabbingFlags[cameraIndex])
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查相机是否已正确打开
|
|
|
|
|
|
if (!IsCameraValid(cameraIndex))
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, "相机未正确初始化", Color.Red);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 准备采集资源
|
|
|
|
|
|
if (!PrepareForGrabbing(cameraIndex))
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, "采集准备失败", Color.Red);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 13:34:31 +08:00
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
// 2. 设置采集标志
|
|
|
|
|
|
grabbingFlags[cameraIndex] = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 创建采集线程
|
|
|
|
|
|
int index = cameraIndex;
|
|
|
|
|
|
grabThreads[cameraIndex] = new Thread(() => GrabThreadProc(index));
|
|
|
|
|
|
grabThreads[cameraIndex].Name = $"CameraGrabThread_{cameraIndex}";
|
|
|
|
|
|
grabThreads[cameraIndex].IsBackground = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 启动线程
|
|
|
|
|
|
grabThreads[cameraIndex].Start();
|
|
|
|
|
|
|
|
|
|
|
|
// 等待线程启动
|
|
|
|
|
|
Thread.Sleep(100);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 开始采集
|
|
|
|
|
|
int ret = cameras[cameraIndex].MV_CC_StartGrabbing_NET();
|
|
|
|
|
|
if (ret != MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"开始采集失败: {ret}", Color.Red);
|
|
|
|
|
|
grabbingFlags[cameraIndex] = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
UpdateCameraStatus(cameraIndex, "正在采集...", Color.Green);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"开始采集异常: {ex.Message}", Color.Red);
|
|
|
|
|
|
Console.WriteLine($"启动相机{cameraIndex + 1}异常: {ex.ToString()}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 检查相机是否有效
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsCameraValid(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return cameras[cameraIndex] != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 停止单个相机采集
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void StopSingleCameraCapture(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cameras[cameraIndex] == null || !grabbingFlags[cameraIndex])
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 停止采集标志
|
|
|
|
|
|
grabbingFlags[cameraIndex] = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 等待采集线程结束
|
|
|
|
|
|
if (grabThreads[cameraIndex] != null && grabThreads[cameraIndex].IsAlive)
|
|
|
|
|
|
{
|
|
|
|
|
|
grabThreads[cameraIndex].Join(1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 停止相机采集
|
|
|
|
|
|
cameras[cameraIndex].MV_CC_StopGrabbing_NET();
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
// 4. 清理资源
|
|
|
|
|
|
CleanupCameraResources(cameraIndex);
|
|
|
|
|
|
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, "已停止采集", Color.Blue);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateCameraStatus(cameraIndex, $"停止采集异常: {ex.Message}", Color.Red);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool PrepareForGrabbing(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取图像参数
|
|
|
|
|
|
MyCamera.MVCC_INTVALUE_EX width = new MyCamera.MVCC_INTVALUE_EX();
|
|
|
|
|
|
int ret = cameras[cameraIndex].MV_CC_GetIntValueEx_NET("Width", ref width);
|
|
|
|
|
|
if (ret != MyCamera.MV_OK) return false;
|
|
|
|
|
|
|
|
|
|
|
|
MyCamera.MVCC_INTVALUE_EX height = new MyCamera.MVCC_INTVALUE_EX();
|
|
|
|
|
|
ret = cameras[cameraIndex].MV_CC_GetIntValueEx_NET("Height", ref height);
|
|
|
|
|
|
if (ret != MyCamera.MV_OK) return false;
|
|
|
|
|
|
|
|
|
|
|
|
MyCamera.MVCC_ENUMVALUE pixelFormat = new MyCamera.MVCC_ENUMVALUE();
|
|
|
|
|
|
ret = cameras[cameraIndex].MV_CC_GetEnumValue_NET("PixelFormat", ref pixelFormat);
|
|
|
|
|
|
if (ret != MyCamera.MV_OK) return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建Bitmap
|
|
|
|
|
|
PixelFormat bitmapFormat = IsMonoPixelFormat(pixelFormat.nCurValue) ?
|
|
|
|
|
|
PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保清理旧的Bitmap
|
|
|
|
|
|
if (displayBitmaps[cameraIndex] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
displayBitmaps[cameraIndex].Dispose();
|
|
|
|
|
|
displayBitmaps[cameraIndex] = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新的Bitmap
|
|
|
|
|
|
displayBitmaps[cameraIndex] = new Bitmap((int)width.nCurValue, (int)height.nCurValue, bitmapFormat);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是8位灰度图,设置调色板
|
|
|
|
|
|
if (bitmapFormat == PixelFormat.Format8bppIndexed)
|
|
|
|
|
|
{
|
|
|
|
|
|
ColorPalette palette = displayBitmaps[cameraIndex].Palette;
|
|
|
|
|
|
for (int i = 0; i < palette.Entries.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
palette.Entries[i] = Color.FromArgb(i, i, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
displayBitmaps[cameraIndex].Palette = palette;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 分配显示缓冲区
|
|
|
|
|
|
uint bufferSize = bitmapFormat == PixelFormat.Format8bppIndexed ?
|
|
|
|
|
|
(uint)(width.nCurValue * height.nCurValue) :
|
|
|
|
|
|
(uint)(width.nCurValue * height.nCurValue * 3);
|
|
|
|
|
|
|
|
|
|
|
|
// 确保清理旧的缓冲区
|
|
|
|
|
|
if (displayBufs[cameraIndex] != IntPtr.Zero)
|
|
|
|
|
|
{
|
|
|
|
|
|
Marshal.FreeHGlobal(displayBufs[cameraIndex]);
|
|
|
|
|
|
displayBufs[cameraIndex] = IntPtr.Zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
displayBufs[cameraIndex] = Marshal.AllocHGlobal((int)bufferSize);
|
|
|
|
|
|
displayBufSizes[cameraIndex] = bufferSize;
|
|
|
|
|
|
|
|
|
|
|
|
frameInfos[cameraIndex] = new MyCamera.MV_FRAME_OUT_INFO_EX();
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"准备相机{cameraIndex + 1}失败: {ex.Message}");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断是否为单色像素格式
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsMonoPixelFormat(uint pixelFormat)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (pixelFormat)
|
|
|
|
|
|
{
|
|
|
|
|
|
case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
|
|
|
|
|
|
case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10:
|
|
|
|
|
|
case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12:
|
|
|
|
|
|
case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
|
|
|
|
|
|
case (uint)MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
|
|
|
|
|
|
return true;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-06 10:34:48 +08:00
|
|
|
|
/// 采集线程处理函数 - 添加视频录制(修改)
|
2026-01-05 19:21:33 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void GrabThreadProc(int cameraIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
MyCamera camera = cameras[cameraIndex];
|
|
|
|
|
|
MyCamera.MV_FRAME_OUT frameOut = new MyCamera.MV_FRAME_OUT();
|
|
|
|
|
|
MyCamera.MV_PIXEL_CONVERT_PARAM convertParam = new MyCamera.MV_PIXEL_CONVERT_PARAM();
|
|
|
|
|
|
MyCamera.MV_DISPLAY_FRAME_INFO displayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO();
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 帧率控制变量(新增)
|
|
|
|
|
|
int framesWritten = 0;
|
|
|
|
|
|
DateTime lastWriteTime = DateTime.Now;
|
|
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
while (grabbingFlags[cameraIndex])
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取图像缓冲区
|
|
|
|
|
|
int ret = camera.MV_CC_GetImageBuffer_NET(ref frameOut, 1000);
|
|
|
|
|
|
if (ret == MyCamera.MV_OK)
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (locks[cameraIndex])
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新帧信息
|
|
|
|
|
|
frameInfos[cameraIndex] = frameOut.stFrameInfo;
|
|
|
|
|
|
|
|
|
|
|
|
// 像素格式转换
|
|
|
|
|
|
convertParam.nWidth = frameOut.stFrameInfo.nWidth;
|
|
|
|
|
|
convertParam.nHeight = frameOut.stFrameInfo.nHeight;
|
|
|
|
|
|
convertParam.enSrcPixelType = frameOut.stFrameInfo.enPixelType;
|
|
|
|
|
|
convertParam.pSrcData = frameOut.pBufAddr;
|
|
|
|
|
|
convertParam.nSrcDataLen = frameOut.stFrameInfo.nFrameLen;
|
|
|
|
|
|
convertParam.pDstBuffer = displayBufs[cameraIndex];
|
|
|
|
|
|
convertParam.nDstBufferSize = displayBufSizes[cameraIndex];
|
|
|
|
|
|
|
|
|
|
|
|
if (displayBitmaps[cameraIndex].PixelFormat == PixelFormat.Format8bppIndexed)
|
|
|
|
|
|
{
|
|
|
|
|
|
convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
convertParam.enDstPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
camera.MV_CC_ConvertPixelType_NET(ref convertParam);
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 更新Bitmap
|
2026-01-05 19:21:33 +08:00
|
|
|
|
BitmapData bitmapData = displayBitmaps[cameraIndex].LockBits(
|
|
|
|
|
|
new Rectangle(0, 0, (int)frameOut.stFrameInfo.nWidth, (int)frameOut.stFrameInfo.nHeight),
|
|
|
|
|
|
ImageLockMode.ReadWrite, displayBitmaps[cameraIndex].PixelFormat);
|
|
|
|
|
|
|
|
|
|
|
|
CopyMemory(bitmapData.Scan0, convertParam.pDstBuffer,
|
|
|
|
|
|
(uint)(bitmapData.Stride * displayBitmaps[cameraIndex].Height));
|
|
|
|
|
|
|
|
|
|
|
|
displayBitmaps[cameraIndex].UnlockBits(bitmapData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 13:34:31 +08:00
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 使用SDK显示
|
2026-01-05 19:21:33 +08:00
|
|
|
|
displayInfo.hWnd = pictureBoxes[cameraIndex].Handle;
|
|
|
|
|
|
displayInfo.pData = frameOut.pBufAddr;
|
|
|
|
|
|
displayInfo.nDataLen = frameOut.stFrameInfo.nFrameLen;
|
|
|
|
|
|
displayInfo.nWidth = frameOut.stFrameInfo.nWidth;
|
|
|
|
|
|
displayInfo.nHeight = frameOut.stFrameInfo.nHeight;
|
|
|
|
|
|
displayInfo.enPixelType = frameOut.stFrameInfo.enPixelType;
|
|
|
|
|
|
|
|
|
|
|
|
// 在UI线程中调用显示
|
|
|
|
|
|
if (this.InvokeRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
this.Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
camera.MV_CC_DisplayOneFrame_NET(ref displayInfo);
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
camera.MV_CC_DisplayOneFrame_NET(ref displayInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 释放图像缓冲区
|
|
|
|
|
|
camera.MV_CC_FreeImageBuffer_NET(ref frameOut);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"相机{cameraIndex + 1}采集异常: {ex.Message}");
|
|
|
|
|
|
// 继续循环,不退出
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
/// <summary>
|
2026-01-06 10:34:48 +08:00
|
|
|
|
/// 清理相机资源(修改)
|
2026-01-05 19:21:33 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CleanupCameraResources(int cameraIndex)
|
|
|
|
|
|
{
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 清理显示缓冲区
|
2026-01-05 19:21:33 +08:00
|
|
|
|
if (displayBufs[cameraIndex] != IntPtr.Zero)
|
|
|
|
|
|
{
|
|
|
|
|
|
Marshal.FreeHGlobal(displayBufs[cameraIndex]);
|
|
|
|
|
|
displayBufs[cameraIndex] = IntPtr.Zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
// 清理Bitmap
|
2026-01-05 19:21:33 +08:00
|
|
|
|
if (displayBitmaps[cameraIndex] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
displayBitmaps[cameraIndex].Dispose();
|
|
|
|
|
|
displayBitmaps[cameraIndex] = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
displayBufSizes[cameraIndex] = 0;
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
2026-01-06 13:34:31 +08:00
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新相机状态显示
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateCameraStatus(int cameraIndex, string message, Color color)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (statusLabels[cameraIndex].InvokeRequired)
|
|
|
|
|
|
{
|
|
|
|
|
|
statusLabels[cameraIndex].Invoke(new Action(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
statusLabels[cameraIndex].Text = $"状态:{message}";
|
|
|
|
|
|
statusLabels[cameraIndex].ForeColor = color;
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
statusLabels[cameraIndex].Text = $"状态:{message}";
|
|
|
|
|
|
statusLabels[cameraIndex].ForeColor = color;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 更新系统状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void UpdateSystemStatus(string message)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 这里可以添加系统状态栏更新逻辑
|
|
|
|
|
|
Console.WriteLine($"系统状态: {message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 窗体关闭时的清理工作
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void AutoCameraForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
// 停止状态检查线程
|
|
|
|
|
|
if (_statusCheckThread != null && _statusCheckThread.IsAlive)
|
|
|
|
|
|
{
|
|
|
|
|
|
_statusCheckThread.Join(1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 停止所有采集线程
|
|
|
|
|
|
for (int i = 0; i < MAX_CAMERAS; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
grabbingFlags[i] = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (grabThreads[i] != null && grabThreads[i].IsAlive)
|
|
|
|
|
|
{
|
|
|
|
|
|
grabThreads[i].Join(1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cameras[i] != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
cameras[i].MV_CC_StopGrabbing_NET();
|
|
|
|
|
|
cameras[i].MV_CC_CloseDevice_NET();
|
|
|
|
|
|
cameras[i].MV_CC_DestroyDevice_NET();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CleanupCameraResources(i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-06 10:34:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-01-05 19:21:33 +08:00
|
|
|
|
// 反初始化SDK
|
|
|
|
|
|
MyCamera.MV_CC_Finalize_NET();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
MessageBox.Show($"清理资源时发生异常: {ex.Message}", "警告",
|
|
|
|
|
|
MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|