AForge.video摄像头深入研究

By admin at 10 天前 • 0人收藏 • 70人看过

AForge 官方提供了一个专门用于显示视频的控件:VideoSourcePlayer(位于 AForge.Controls 命名空间中)。

它内部已经处理好了所有的跨线程、重绘和双缓冲问题,直接将 VideoDevice 赋值给它的 VideoSource 属性即可自动播放,不需要手动去写 OnNewFrame 的克隆和释放逻辑,开发起来更加省心


image.png


using AForge;
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        VideoCaptureDevice VideoDevice;

        // 新增:记录镜像状态的变量
        private bool isMirrorX = false; // 左右镜像
        private bool isMirrorY = false; // 上下镜像
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            FilterInfoCollection VideoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            if (VideoDevices.Count > 0)
            {
                VideoDevice = new VideoCaptureDevice(VideoDevices[0].MonikerString);

                // 确保摄像头确实返回了支持的分辨率列表
                if (VideoDevice.VideoCapabilities.Length > 0)
                {
                    // 自动寻找最高分辨率和最高帧率的配置
                    var bestResolution = VideoDevice.VideoCapabilities
                        .OrderByDescending(c => c.FrameSize.Width * c.FrameSize.Height) // 优先按面积(像素总数)降序排
                        .ThenByDescending(c => c.MaximumFrameRate)                      // 分辨率相同时,按最大帧率降序排
                        .First();                                                       // 取出排在第一位的(也就是最好的)

                    // 赋值给摄像头
                    VideoDevice.VideoResolution = bestResolution;

                    // 打印出来确认一下
                    Console.WriteLine($"已自动选择最佳画质: {bestResolution.FrameSize.Width} x {bestResolution.FrameSize.Height} @ {bestResolution.MaximumFrameRate} fps");
                }


                // 假设你拖了一个 VideoSourcePlayer 控件到窗体上,命名为 videoSourcePlayer1
                videoSourcePlayer1.VideoSource = VideoDevice;

                // 订阅控件的 NewFrame 事件
                videoSourcePlayer1.NewFrame += VideoSourcePlayer1_NewFrame;

                // 启动播放器 (不需要调用 VideoDevice.Start())
                videoSourcePlayer1.Start();
            }
        }

        private void VideoSourcePlayer1_NewFrame(object sender, ref Bitmap image)
        {
            // 确定翻转类型
            RotateFlipType flipType = RotateFlipType.RotateNoneFlipNone;

            if (isMirrorX && isMirrorY)
                flipType = RotateFlipType.RotateNoneFlipXY;
            else if (isMirrorX)
                flipType = RotateFlipType.RotateNoneFlipX;
            else if (isMirrorY)
                flipType = RotateFlipType.RotateNoneFlipY;

            // 如果需要镜像,直接对 ref 传入的 image 进行翻转
            if (flipType != RotateFlipType.RotateNoneFlipNone)
            {
                image.RotateFlip(flipType);
            }

            // 注意:不要在这里调用 image.Dispose()!
            // 也不需要写任何 BeginInvoke 代码。
            // 处理完后,控件会自动把处理后的图画到界面上,并自己负责释放内存。
        }

 

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // 确保播放器正在运行才去停止它
            if (videoSourcePlayer1 != null && videoSourcePlayer1.IsRunning)
            {
                // 1. 发送停止信号(非阻塞,告诉后台线程准备退出)
                videoSourcePlayer1.SignalToStop();

                // 2. 等待后台线程安全结束当前帧的处理并关闭硬件连接
                videoSourcePlayer1.WaitForStop();
            }
        }

        // 提取一个公共的停止方法
      
        // 左右镜像复选框的事件
        private void chkMirrorX_CheckedChanged(object sender, EventArgs e)
        {
            isMirrorX = chkMirrorX.Checked;
        }

        // 上下镜像复选框的事件
        private void chkMirrorY_CheckedChanged(object sender, EventArgs e)
        {
            isMirrorY = chkMirrorY.Checked;
        }
    }
}


3 个回复 | 最后更新于 6 天前
10 天前   #1

image.png

为了实现与界面的完美解耦支持多摄像头独立配置以及统一生命周期管理,我让AI封装了一个独立的类库

用起来就很简单了

using AForge;
using AForge.Video;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 示例:打开第一个摄像头 (索引 0),水平左右镜像 (2),画面塞进 panel1
            AForgeCameraManager.InitCamera(0, 3, panel1);

            // 示例:假设你有第二个摄像头,打开它 (索引 1),不镜像 (0),画面塞进 panel2
             AForgeCameraManager.InitCamera(1, 1, panel2);
        }

 

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            AForgeCameraManager.CloseAllCameras();
        }

    }
}

AForgeCameraManager.CS 类库代码如下:

using AForge.Controls;
using AForge.Video.DirectShow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public class AForgeCameraManager
    {
        // 静态列表:用于记录当前打开的所有摄像头播放器实例,方便统一关闭
        private static List<VideoSourcePlayer> activePlayers = new List<VideoSourcePlayer>();

        /// <summary>
        /// 1. 初始化摄像头并在指定控件中显示
        /// </summary>
        /// <param name="deviceIndex">摄像头端口号(索引,从0开始)</param>
        /// <param name="mirrorType">镜像方向:0=不镜像, 1=上下, 2=左右, 3=上下左右</param>
        /// <param name="placeholder">占位控件(例如 Panel,用于决定摄像头画面的位置和大小)</param>
        public static void InitCamera(int deviceIndex, int mirrorType, Control placeholder)
        {
            // 1. 获取所有视频输入设备
            FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            if (videoDevices.Count == 0)
            {
                MessageBox.Show("未检测到任何摄像头设备!", "提示");
                return;
            }

            if (deviceIndex < 0 || deviceIndex >= videoDevices.Count)
            {
                MessageBox.Show($"找不到索引为 {deviceIndex} 的摄像头!", "提示");
                return;
            }

            // 2. 实例化对应摄像头
            VideoCaptureDevice videoDevice = new VideoCaptureDevice(videoDevices[deviceIndex].MonikerString);

            // 3. 自动寻找最佳画质
            if (videoDevice.VideoCapabilities.Length > 0)
            {
                var bestResolution = videoDevice.VideoCapabilities
                    .OrderByDescending(c => c.FrameSize.Width * c.FrameSize.Height)
                    .ThenByDescending(c => c.MaximumFrameRate)
                    .First();

                videoDevice.VideoResolution = bestResolution;
                Console.WriteLine($"摄像头[{deviceIndex}] 选择画质: {bestResolution.FrameSize.Width}x{bestResolution.FrameSize.Height} @ {bestResolution.MaximumFrameRate}fps");
            }

            // 4. 动态创建视频播放控件,解耦UI
            VideoSourcePlayer player = new VideoSourcePlayer();
            player.VideoSource = videoDevice;

            // 设置停靠方式为填满占位控件
            player.Dock = DockStyle.Fill;

            // 5. 解析并应用镜像设置
            RotateFlipType flipType = RotateFlipType.RotateNoneFlipNone;
            switch (mirrorType)
            {
                case 1: flipType = RotateFlipType.RotateNoneFlipY; break;   // 上下
                case 2: flipType = RotateFlipType.RotateNoneFlipX; break;   // 左右
                case 3: flipType = RotateFlipType.RotateNoneFlipXY; break;  // 上下左右
            }

            // 只有需要翻转时才订阅事件,节省性能
            if (flipType != RotateFlipType.RotateNoneFlipNone)
            {
                // 使用匿名委托(Lambda)绑定事件,这样每个摄像头都能独立记住自己的翻转方式,互不干扰
                player.NewFrame += (object sender, ref Bitmap image) =>
                {
                    image.RotateFlip(flipType);
                };
            }

            // 6. 将控件加入占位符,并启动
            // 清空占位控件中可能残留的其他内容
            placeholder.Controls.Clear();
            placeholder.Controls.Add(player);

            // 记录到静态列表中
            activePlayers.Add(player);

            // 启动播放
            player.Start();
        }

        /// <summary>
        /// 2. 关闭全部已知打开的摄像头
        /// </summary>
        public static void CloseAllCameras()
        {
            foreach (var player in activePlayers)
            {
                if (player != null && player.IsRunning)
                {
                    // 发送停止信号
                    player.SignalToStop();
                    // 阻塞等待当前帧处理完毕并安全释放硬件连接
                    player.WaitForStop();
                }

                // 从 UI 占位控件中移除并释放资源
                if (player != null && !player.IsDisposed)
                {
                    player.Dispose();
                }
            }

            // 清空追踪列表
            activePlayers.Clear();
            Console.WriteLine("所有摄像头已安全关闭。");
        }
    }
}

完整工程:

WindowsFormsApp2.zip


6 天前   #2

ai还是方便的

6 天前   #3

回复#2 @aubreychao :

那是相当方便啊, 很多东西自己写写改改估计要一个上午, ai几分钟改好

登录后方可回帖

登 录
信息栏
本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网
Labview VI 程序框图在线解锁工具

才仁机械


网站地图SiteMap

Loading...