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; using Accord.Video; using Accord.Video.FFMPEG; 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; // 视频保存配置(新增) private string _videoSavePath = @"D:\CameraRecordings\"; // 修改为你的保存路径 private double _frameRate = 30.0; // 默认帧率 // 构造函数 public AutoCameraForm(GetRunStatusDelegate getRunStatusCallback, ReturnToPreviousFormDelegate returnCallback = null, string videoSavePath = null) { _getRunStatusCallback = getRunStatusCallback; // 如果提供了返回回调,就订阅 if (returnCallback != null) { OnReturnRequested += returnCallback; } //// 设置视频保存路径 //if (!string.IsNullOrEmpty(videoSavePath)) //{ // _videoSavePath = videoSavePath; //} //// 确保保存目录存在 //Directory.CreateDirectory(_videoSavePath); 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(); } /// /// 初始化UI界面 /// 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); } /// /// 初始化相机系统 /// 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); } } /// /// 启动状态检查线程 /// private void StartStatusCheckThread() { _statusCheckThread = new Thread(CheckRunStatusLoop) { IsBackground = true }; _statusCheckThread.Start(); } /// /// 状态检查循环 - 添加异常处理 /// 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 { // 忽略异常 } } } /// /// 开始所有相机采集 /// private void StartAllCamerasCapture() { if (!_camerasInitialized) return; UpdateSystemStatus("测试开始,正在启动相机采集..."); for (int i = 0; i < MAX_CAMERAS; i++) { if (cameras[i] != null && !grabbingFlags[i]) { StartSingleCameraCapture(i); } } UpdateSystemStatus("所有相机已开始采集"); } /// /// 停止所有相机采集 /// private void StopAllCamerasCapture() { if (!_camerasInitialized) return; UpdateSystemStatus("测试结束,正在停止相机采集..."); for (int i = 0; i < MAX_CAMERAS; i++) { if (cameras[i] != null && grabbingFlags[i]) { StopSingleCameraCapture(i); } } UpdateSystemStatus("所有相机已停止采集"); } /// /// 初始化单个相机 /// 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; } // 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; } UpdateCameraStatus(cameraIndex, "正在采集...", Color.Green); } catch (Exception ex) { UpdateCameraStatus(cameraIndex, $"开始采集异常: {ex.Message}", Color.Red); Console.WriteLine($"启动相机{cameraIndex + 1}异常: {ex.ToString()}"); } } /// /// 检查相机是否有效 /// private bool IsCameraValid(int cameraIndex) { try { return cameras[cameraIndex] != null; } catch { return false; } } /// /// 停止单个相机采集 /// 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(); // 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; } } /// /// 判断是否为单色像素格式 /// 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; } } /// /// 采集线程处理函数 - 添加视频录制(修改) /// 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(); // 帧率控制变量(新增) int framesWritten = 0; DateTime lastWriteTime = DateTime.Now; 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); // 更新Bitmap 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); } // 使用SDK显示 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}"); // 继续循环,不退出 } } } /// /// 清理相机资源(修改) /// private void CleanupCameraResources(int cameraIndex) { // 清理显示缓冲区 if (displayBufs[cameraIndex] != IntPtr.Zero) { Marshal.FreeHGlobal(displayBufs[cameraIndex]); displayBufs[cameraIndex] = IntPtr.Zero; } // 清理Bitmap if (displayBitmaps[cameraIndex] != null) { displayBitmaps[cameraIndex].Dispose(); displayBitmaps[cameraIndex] = null; } displayBufSizes[cameraIndex] = 0; } /// /// 更新相机状态显示 /// 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; } } /// /// 更新系统状态 /// private void UpdateSystemStatus(string message) { // 这里可以添加系统状态栏更新逻辑 Console.WriteLine($"系统状态: {message}"); } /// /// 窗体关闭时的清理工作 /// 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); } // 反初始化SDK MyCamera.MV_CC_Finalize_NET(); } catch (Exception ex) { MessageBox.Show($"清理资源时发生异常: {ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }