diff --git a/App.config b/App.config index 7bdf230..327f312 100644 --- a/App.config +++ b/App.config @@ -41,6 +41,9 @@ + + + diff --git a/ConfigHelper.cs b/ConfigHelper.cs new file mode 100644 index 0000000..4ec4a63 --- /dev/null +++ b/ConfigHelper.cs @@ -0,0 +1,66 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace videoGather +{ + public static class ConfigHelper + { + + private static readonly string ConfigFolder = + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config"); + + /// + /// 保存配置到指定名称的配置文件 + /// + /// 配置数据类型 + /// 配置文件名(无需扩展名) + /// 配置数据 + public static void Save(string configName, T data) + { + if (!Directory.Exists(ConfigFolder)) + { + Directory.CreateDirectory(ConfigFolder); + } + + var filePath = GetConfigPath(configName); + var json = JsonConvert.SerializeObject(data, Formatting.Indented); + File.WriteAllText(filePath, json); + } + + /// + /// 读取指定配置文件的配置数据 + /// + /// 配置数据类型 + /// 配置文件名(无需扩展名) + /// 当配置不存在时的默认值 + public static T Load(string configName, T defaultValue = default) + { + var filePath = GetConfigPath(configName); + + if (!File.Exists(filePath)) + { + return defaultValue; + } + + try + { + var json = File.ReadAllText(filePath); + return JsonConvert.DeserializeObject(json); + } + catch + { + return defaultValue; + } + } + + private static string GetConfigPath(string configName) + { + return Path.Combine(ConfigFolder, $"{configName}.config.json"); + } + } +} diff --git a/DoubleBufferedPanel.cs b/DoubleBufferedPanel.cs new file mode 100644 index 0000000..4052d62 --- /dev/null +++ b/DoubleBufferedPanel.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace videoGather +{ + public class DoubleBufferedPanel : Panel + { + + private Bitmap _backImage; + // 新增背景图属性 + public Bitmap BackImage + { + get => _backImage; + set + { + if (_backImage != null) + { + _backImage.Dispose(); + } + _backImage = value; + Invalidate(); // 触发重绘 + } + } + // 构造函数 + public DoubleBufferedPanel() + { + // 启用双缓冲 + this.DoubleBuffered = true; // 内置支持 + // 设置样式以避免闪烁 + this.SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint, true); + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + // 禁用默认背景绘制(关键!) + // base.OnPaintBackground(e); // 注释掉这行 + } + + protected override void OnPaint(PaintEventArgs e) + { + // 绘制自定义背景图 + if (_backImage != null) + { + e.Graphics.DrawImage(_backImage, ClientRectangle); + } + + base.OnPaint(e); // 保持原有绘制逻辑 + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _backImage?.Dispose(); + } + base.Dispose(disposing); + } + } +} diff --git a/SelectionConfig .cs b/SelectionConfig .cs new file mode 100644 index 0000000..5144ca6 --- /dev/null +++ b/SelectionConfig .cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace videoGather +{ + public class SelectionConfig + { + + public int X { get; set; } + public int Y { get; set; } + public int Width { get; set; } + public int Height { get; set; } + + // 添加时间戳用于校验 + public DateTime LastModified { get; set; } = DateTime.Now; + } +} diff --git a/video.Designer.cs b/video.Designer.cs index fd9c122..120ba6c 100644 --- a/video.Designer.cs +++ b/video.Designer.cs @@ -34,6 +34,7 @@ namespace videoGather this.btn_Capture = new System.Windows.Forms.Button(); this.btn_Close = new System.Windows.Forms.Button(); this.gb_ButtonBox = new System.Windows.Forms.GroupBox(); + this.button7 = new System.Windows.Forms.Button(); this.textBox1 = new System.Windows.Forms.TextBox(); this.label7 = new System.Windows.Forms.Label(); this.button6 = new System.Windows.Forms.Button(); @@ -73,7 +74,7 @@ namespace videoGather this.jianchamingcheng = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.kaifangsi = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.gb_CameraPanel = new System.Windows.Forms.GroupBox(); - this.vsp_Panel = new AForge.Controls.VideoSourcePlayer(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.gb_ButtonBox.SuspendLayout(); this.groupBox2.SuspendLayout(); @@ -81,6 +82,7 @@ namespace videoGather this.groupBox1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dGVPatient)).BeginInit(); this.gb_CameraPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // cb_CameraSelect @@ -118,6 +120,7 @@ namespace videoGather // // gb_ButtonBox // + this.gb_ButtonBox.Controls.Add(this.button7); this.gb_ButtonBox.Controls.Add(this.textBox1); this.gb_ButtonBox.Controls.Add(this.label7); this.gb_ButtonBox.Controls.Add(this.button6); @@ -141,12 +144,22 @@ namespace videoGather this.gb_ButtonBox.Margin = new System.Windows.Forms.Padding(2); this.gb_ButtonBox.Name = "gb_ButtonBox"; this.gb_ButtonBox.Padding = new System.Windows.Forms.Padding(2); - this.gb_ButtonBox.Size = new System.Drawing.Size(556, 235); + this.gb_ButtonBox.Size = new System.Drawing.Size(503, 235); this.gb_ButtonBox.TabIndex = 16; this.gb_ButtonBox.TabStop = false; this.gb_ButtonBox.Text = "功能区"; this.gb_ButtonBox.Enter += new System.EventHandler(this.gb_ButtonBox_Enter); // + // button7 + // + this.button7.Location = new System.Drawing.Point(255, 60); + this.button7.Name = "button7"; + this.button7.Size = new System.Drawing.Size(111, 30); + this.button7.TabIndex = 33; + this.button7.Text = "选中区域"; + this.button7.UseVisualStyleBackColor = true; + this.button7.Click += new System.EventHandler(this.button7_Click_1); + // // textBox1 // this.textBox1.Location = new System.Drawing.Point(123, 175); @@ -168,7 +181,7 @@ namespace videoGather // button6 // this.button6.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button6.Location = new System.Drawing.Point(400, 173); + this.button6.Location = new System.Drawing.Point(371, 173); this.button6.Margin = new System.Windows.Forms.Padding(2); this.button6.Name = "button6"; this.button6.Size = new System.Drawing.Size(127, 31); @@ -205,7 +218,7 @@ namespace videoGather // button1 // this.button1.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button1.Location = new System.Drawing.Point(400, 139); + this.button1.Location = new System.Drawing.Point(371, 139); this.button1.Margin = new System.Windows.Forms.Padding(2); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(127, 31); @@ -227,7 +240,7 @@ namespace videoGather // button4 // this.button4.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button4.Location = new System.Drawing.Point(400, 23); + this.button4.Location = new System.Drawing.Point(371, 23); this.button4.Margin = new System.Windows.Forms.Padding(2); this.button4.Name = "button4"; this.button4.Size = new System.Drawing.Size(127, 112); @@ -255,6 +268,7 @@ namespace videoGather this.lab_Time.Size = new System.Drawing.Size(87, 16); this.lab_Time.TabIndex = 13; this.lab_Time.Text = "00:00:00"; + this.lab_Time.Visible = false; // // label2 // @@ -265,6 +279,7 @@ namespace videoGather this.label2.Size = new System.Drawing.Size(87, 16); this.label2.TabIndex = 12; this.label2.Text = "录制时间:"; + this.label2.Visible = false; // // btn_EndVideo // @@ -275,6 +290,7 @@ namespace videoGather this.btn_EndVideo.TabIndex = 11; this.btn_EndVideo.Text = "停止录像(F8)"; this.btn_EndVideo.UseVisualStyleBackColor = true; + this.btn_EndVideo.Visible = false; this.btn_EndVideo.Click += new System.EventHandler(this.btn_EndVideo_Click); // // btn_PauseVideo @@ -286,6 +302,7 @@ namespace videoGather this.btn_PauseVideo.TabIndex = 10; this.btn_PauseVideo.Text = "暂停录像(F7)"; this.btn_PauseVideo.UseVisualStyleBackColor = true; + this.btn_PauseVideo.Visible = false; this.btn_PauseVideo.Click += new System.EventHandler(this.btn_PauseVideo_Click); // // btn_StartVideo @@ -297,6 +314,7 @@ namespace videoGather this.btn_StartVideo.TabIndex = 9; this.btn_StartVideo.Text = "开始录像(F6)"; this.btn_StartVideo.UseVisualStyleBackColor = true; + this.btn_StartVideo.Visible = false; this.btn_StartVideo.Click += new System.EventHandler(this.btn_StartVideo_Click); // // groupBox2 @@ -320,7 +338,7 @@ namespace videoGather this.groupBox2.Font = new System.Drawing.Font("宋体", 12F); this.groupBox2.Location = new System.Drawing.Point(3, 17); this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(556, 153); + this.groupBox2.Size = new System.Drawing.Size(503, 153); this.groupBox2.TabIndex = 18; this.groupBox2.TabStop = false; this.groupBox2.Text = "病理"; @@ -328,7 +346,7 @@ namespace videoGather // button5 // this.button5.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.button5.Location = new System.Drawing.Point(388, 98); + this.button5.Location = new System.Drawing.Point(371, 98); this.button5.Margin = new System.Windows.Forms.Padding(2); this.button5.Name = "button5"; this.button5.Size = new System.Drawing.Size(127, 31); @@ -465,7 +483,7 @@ namespace videoGather this.groupBox3.Dock = System.Windows.Forms.DockStyle.Left; this.groupBox3.Location = new System.Drawing.Point(0, 0); this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(562, 994); + this.groupBox3.Size = new System.Drawing.Size(509, 994); this.groupBox3.TabIndex = 19; this.groupBox3.TabStop = false; this.groupBox3.Text = "操作"; @@ -479,7 +497,7 @@ namespace videoGather this.groupBox1.Margin = new System.Windows.Forms.Padding(2); this.groupBox1.Name = "groupBox1"; this.groupBox1.Padding = new System.Windows.Forms.Padding(2); - this.groupBox1.Size = new System.Drawing.Size(556, 586); + this.groupBox1.Size = new System.Drawing.Size(503, 586); this.groupBox1.TabIndex = 19; this.groupBox1.TabStop = false; this.groupBox1.Text = "患者信息"; @@ -500,7 +518,7 @@ namespace videoGather this.dGVPatient.ReadOnly = true; this.dGVPatient.RowTemplate.Height = 23; this.dGVPatient.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dGVPatient.Size = new System.Drawing.Size(552, 563); + this.dGVPatient.Size = new System.Drawing.Size(499, 563); this.dGVPatient.TabIndex = 0; this.dGVPatient.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dGVPatient_CellDoubleClick); // @@ -551,27 +569,28 @@ namespace videoGather // // gb_CameraPanel // - this.gb_CameraPanel.Controls.Add(this.vsp_Panel); + this.gb_CameraPanel.Controls.Add(this.pictureBox1); this.gb_CameraPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.gb_CameraPanel.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.gb_CameraPanel.Location = new System.Drawing.Point(562, 0); + this.gb_CameraPanel.Location = new System.Drawing.Point(509, 0); this.gb_CameraPanel.Margin = new System.Windows.Forms.Padding(2); this.gb_CameraPanel.Name = "gb_CameraPanel"; this.gb_CameraPanel.Padding = new System.Windows.Forms.Padding(2); - this.gb_CameraPanel.Size = new System.Drawing.Size(1153, 994); + this.gb_CameraPanel.Size = new System.Drawing.Size(1206, 994); this.gb_CameraPanel.TabIndex = 20; this.gb_CameraPanel.TabStop = false; this.gb_CameraPanel.Text = "拍摄"; // - // vsp_Panel + // pictureBox1 // - this.vsp_Panel.Dock = System.Windows.Forms.DockStyle.Fill; - this.vsp_Panel.Location = new System.Drawing.Point(2, 21); - this.vsp_Panel.Margin = new System.Windows.Forms.Padding(2); - this.vsp_Panel.Name = "vsp_Panel"; - this.vsp_Panel.Size = new System.Drawing.Size(1149, 971); - this.vsp_Panel.TabIndex = 1; - this.vsp_Panel.VideoSource = null; + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.BackColor = System.Drawing.SystemColors.ButtonShadow; + this.pictureBox1.Location = new System.Drawing.Point(15, 17); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(1186, 720); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + this.pictureBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint); // // backgroundWorker1 // @@ -592,7 +611,8 @@ namespace videoGather this.Margin = new System.Windows.Forms.Padding(2); this.Name = "video"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "视频采集"; + this.Text = "视频采集V6"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); this.Load += new System.EventHandler(this.Form1_Load); this.gb_ButtonBox.ResumeLayout(false); @@ -604,6 +624,7 @@ namespace videoGather this.groupBox1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.dGVPatient)).EndInit(); this.gb_CameraPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); } @@ -636,7 +657,6 @@ namespace videoGather private System.Windows.Forms.GroupBox groupBox3; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.GroupBox gb_CameraPanel; - private AForge.Controls.VideoSourcePlayer vsp_Panel; private System.Windows.Forms.DataGridView dGVPatient; private System.Windows.Forms.DataGridViewTextBoxColumn Column1; private System.Windows.Forms.DataGridViewTextBoxColumn name; @@ -654,6 +674,8 @@ namespace videoGather private System.Windows.Forms.Button button6; private System.Windows.Forms.TextBox textBox1; private System.Windows.Forms.Label label7; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Button button7; } } diff --git a/video.cs b/video.cs index a13e589..74d292d 100644 --- a/video.cs +++ b/video.cs @@ -1,6 +1,9 @@  +using AForge.Controls; using AForge.Imaging; +using AForge.Video; using AForge.Video.FFMPEG; +using DirectShowLib; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -11,6 +14,7 @@ using System.Configuration; using System.Data; using System.Diagnostics; using System.Drawing; +using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Linq; @@ -19,6 +23,7 @@ using System.Net.Http; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Security.Policy; using System.Text; using System.Threading; @@ -33,11 +38,12 @@ using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace videoGather { - public partial class video : Form + public partial class video : Form, ISampleGrabberCB { public video() { InitializeComponent(); + InitializeOverlay(); // 初始化透明遮罩层 } #region 属性 /// @@ -129,6 +135,11 @@ namespace videoGather /// 是否开启体检编码扫码 /// private string _isInspect = string.Empty; + /// + /// 是否开启区域截图 + /// + private string _isselectionXY = string.Empty; + //超声存放文件访问地址 ArrayList imagearray = new ArrayList(); /// @@ -139,6 +150,36 @@ namespace videoGather /// 存放自己新增的数据源 /// List hisInfos = new List(); + + AForge.Controls.VideoSourcePlayer player; + + #region 新的方式 + DsDevice device1; + private int _pWidth = 1280; + private int _pHeight = 720; + private int _pStride = 0; + private int _pFPS = 30; + private volatile bool isGrab = false; + + IVideoWindow videoWindow = null; + IMediaControl mediaControl = null; + IFilterGraph2 graphBuilder = null; + ICaptureGraphBuilder2 captureGraphBuilder = null; + + + DsROTEntry rot = null; + + DsDevice[] devices; + #endregion + + #region 选区 + // 类成员变量声明 + private System.Drawing.Rectangle selectionRect; // 存储当前选区矩形 + private Point startPoint; // 选区起始坐标点 + private bool isSelecting; // 是否处于选区状态标志 + private DoubleBufferedPanel overlayPanel; // 透明遮罩层控件 + #endregion + #endregion #region 窗口加载与关闭 @@ -149,10 +190,11 @@ namespace videoGather /// private void Form1_Load(object sender, EventArgs e) { - + // _pWidth = pictureBox1.Width; + // _pHeight = pictureBox1.Height; dGVPatient.AutoGenerateColumns = false; - InitCamera(); + // InitCamera(); InitCameraSelect(); //读取配置信息 _isInspect = ConfigurationManager.AppSettings["isInspect"]; @@ -166,6 +208,7 @@ namespace videoGather _xywh = ConfigurationManager.AppSettings["xywh"]; _model = ConfigurationManager.AppSettings["model"]; _ishisinfo = ConfigurationManager.AppSettings["ishisinfo"]; + _isselectionXY = ConfigurationManager.AppSettings["isselectionXY"]; if (_type == "1") { @@ -198,6 +241,18 @@ namespace videoGather label7.Visible = false; textBox1.Visible = false; } + if (_isselectionXY=="1") + { + button7.Visible = true; + } + else + { + button7.Visible = false; + } + // 启用双缓冲减少闪烁 + typeof(System.Windows.Forms.PictureBox).InvokeMember("DoubleBuffered", + BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, + null, pictureBox1, new object[] { true }); //秒表 timer_count = new System.Timers.Timer(); //实例化Timer类,设置间隔时间为1000毫秒; @@ -217,9 +272,43 @@ namespace videoGather } label4.Focus(); // 添加对窗口大小改变事件的订阅 - this.Resize += new EventHandler(Form1_Resize); + // this.Resize += new EventHandler(Form1_Resize); } + #region 初始化透明遮罩层 + /// + /// 初始化透明遮罩层 + /// + private void InitializeOverlay() + { + // 创建透明遮罩层 + overlayPanel = new DoubleBufferedPanel + { + // 设置与 PictureBox 相同的尺寸和位置 + // Size = pictureBox1.Size, + Location = pictureBox1.Location, + // 设置透明背景 + BackColor = Color.FromArgb(0, 0, 0, 0), + // 设置十字光标 + Cursor = Cursors.Cross, + // 初始不可见 + Visible = false, + Width= 1280, + Height=720 + }; + + // 将遮罩层添加到PictureBox的父容器(确保层级正确) + pictureBox1.Parent.Controls.Add(overlayPanel); + // 置于顶层,保证在视频上方显示 + overlayPanel.BringToFront(); + + // 绑定事件处理器 + overlayPanel.MouseDown += Overlay_MouseDown; + overlayPanel.MouseMove += Overlay_MouseMove; + overlayPanel.MouseUp += Overlay_MouseUp; + overlayPanel.Paint += Overlay_Paint; + } + #endregion private void Form1_Resize(object sender, EventArgs e) { //// 检查视频捕获是否已初始化 @@ -260,7 +349,8 @@ namespace videoGather /// private void Form1_FormClosing(object sender, FormClosingEventArgs e) { - EndCamera(); + // EndCamera(); + StopCamera(); } #endregion @@ -285,20 +375,31 @@ namespace videoGather public void InitCameraSelect() { this.cb_CameraSelect.DropDownStyle = ComboBoxStyle.DropDownList; - //根据读取到的摄像头加载选择项 - foreach (AForge.Video.DirectShow.FilterInfo item in infoCollection) - { - this.cb_CameraSelect.Items.Add(item.Name); - } - //for (int i = 1; i <= infoCollection.Count; i++) + ////根据读取到的摄像头加载选择项 + //foreach (AForge.Video.DirectShow.FilterInfo item in infoCollection) //{ - // this.cb_CameraSelect.Items.Add("摄像头" + i); + // this.cb_CameraSelect.Items.Add(item.Name); //} + ////for (int i = 1; i <= infoCollection.Count; i++) + ////{ + //// this.cb_CameraSelect.Items.Add("摄像头" + i); + ////} + //if (cb_CameraSelect.Items.Count > 0) + //{ + // cb_CameraSelect.SelectedIndex = 0; + //} + + devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); + for (int i = 0; i < devices.Length; i++) + { + this.cb_CameraSelect.Items.Add(devices[i].Name); + + } if (cb_CameraSelect.Items.Count > 0) { - cb_CameraSelect.SelectedIndex = 0; + cb_CameraSelect.SelectedIndex = 1; + //} } - } #endregion @@ -310,24 +411,89 @@ namespace videoGather /// private void btn_Connect_Click(object sender, EventArgs e) { - var CameraSelect = this.cb_CameraSelect.Text; - var selectIndex = this.cb_CameraSelect.SelectedIndex; - if (string.IsNullOrEmpty(CameraSelect)) + // var CameraSelect = this.cb_CameraSelect.Text; + // var selectIndex = this.cb_CameraSelect.SelectedIndex; + // if (string.IsNullOrEmpty(CameraSelect)) + // { + // MessageBox.Show("请先选择视频源"); + // return; + // } + + + // //已有连接的摄像头时先关闭 + // if (videoCapture != null) + // { + // EndCamera(); + // } + + // player = new VideoSourcePlayer(); + // player.Dock = DockStyle.Fill; + // gb_CameraPanel.Controls.Add(player); + + + // videoCapture = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex].MonikerString); + // videoCapture.VideoResolution = videoCapture.VideoCapabilities + //.OrderBy(x => x.MaximumFrameRate) + //.FirstOrDefault(); + + // //启动摄像头 + // player.VideoSource = videoCapture; + // player.Start(); + // // videoCapture.Start(); + // //this.vsp_Panel.VideoSource.NewFrame += videoCapture_NewFrame; + // //this.vsp_Panel.VideoSource. + + StopCamera(); + // 调用前检查 + if (!ValidateResolution(_pWidth, _pHeight)) { - MessageBox.Show("请先选择视频源"); + MessageBox.Show("不支持的视频分辨率"); return; } - //已有连接的摄像头时先关闭 - if (videoCapture != null) + + if (devices.Length == 0) { - EndCamera(); + MessageBox.Show("无视频源"); + return; + } + else + { + DsDevice ds=null; + foreach (var item in devices) + { + if(item.Name== this.cb_CameraSelect.SelectedItem) + { + ds = item; + break; + } + } + + CaptureVideo(ds); } - videoCapture = new AForge.Video.DirectShow.VideoCaptureDevice(infoCollection[selectIndex].MonikerString); - //启动摄像头 - this.vsp_Panel.VideoSource = videoCapture; - this.vsp_Panel.Start(); this.btn_Capture.Enabled = true; + + } + + + /// + /// 检查是否支持分辨率 + /// + /// + /// + /// + private bool ValidateResolution(int width, int height) + { + // 检查是否为设备支持的分辨率 + var supportedResolutions = new[] { + new Size(640, 480), + new Size(1280, 720), + new Size(1920, 1080) + }; + return supportedResolutions.Any(s => s.Width == width && s.Height == height); + } + + /// /// 关闭按钮 /// @@ -336,23 +502,53 @@ namespace videoGather private void btn_Close_Click(object sender, EventArgs e) { HookStop(); - EndCamera(); + // EndCamera(); + StopCamera(); } /// /// 关闭摄像头 /// public void EndCamera() { - if (this.vsp_Panel.VideoSource != null) + if (videoCapture != null) { - //也可以用 Stop() 方法关闭 - //指示灯停止且等待 - this.vsp_Panel.SignalToStop(); - //停止且等待 - this.vsp_Panel.WaitForStop(); - this.vsp_Panel.VideoSource = null; + // 先移除事件处理器 + // this.vsp_Panel.VideoSource.NewFrame -= videoCapture_NewFrame; + if (player!=null) + { + + videoCapture.SignalToStop(); + videoCapture.WaitForStop(); + videoCapture.Stop(); + + // 停止视频源 + player.SignalToStop(); + player.WaitForStop(); + player.Stop(); + gb_CameraPanel.Controls.Clear(); + videoCapture = null; + player = null; + + + } + } + //if (this.vsp_Panel.VideoSource != null) + //{ + // //也可以用 Stop() 方法关闭 + // //指示灯停止且等待 + // //this.vsp_Panel.SignalToStop(); + // ////停止且等待 + // //this.vsp_Panel.WaitForStop(); + // //this.vsp_Panel.VideoSource = null; + // // this.vsp_Panel.Stop(); + // // this.vsp_Panel.Dispose(); + // // this.vsp_Panel.VideoSource = null; + + + + //} } #endregion @@ -366,29 +562,34 @@ namespace videoGather { try { - if (videoCapture == null) - { - MessageBox.Show("设备未连接"); - return; - } - imgMap = this.vsp_Panel.GetCurrentVideoFrame(); - if (imgMap == null) - { - return; - } - var saveName = $"{filePath}{$"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}"}.jpg"; - imgMap.Save(saveName); - imagearray.Add(saveName); - // 使用 Invoke 方法确保在UI线程上更新标签的文本 - if (label1.InvokeRequired) - { - label1.Invoke(new Action(() => label1.Text = imagearray.Count.ToString())); - } - else - { - label1.Text = imagearray.Count.ToString(); - } - label1.Refresh(); + #region 之前 + //if (videoCapture == null) + //{ + // MessageBox.Show("设备未连接"); + // return; + //} + //imgMap = player.GetCurrentVideoFrame(); + //if (imgMap == null) + //{ + // return; + //} + //var saveName = $"{filePath}{$"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}"}.jpg"; + //imgMap.Save(saveName); + //imgMap = null; + //imagearray.Add(saveName); + //// 使用 Invoke 方法确保在UI线程上更新标签的文本 + //if (label1.InvokeRequired) + //{ + // label1.Invoke(new Action(() => label1.Text = imagearray.Count.ToString())); + //} + //else + //{ + // label1.Text = imagearray.Count.ToString(); + //} + //label1.Refresh(); + #endregion + + isGrab = true; } catch (Exception ex) { @@ -1108,7 +1309,7 @@ namespace videoGather /// private void SaveImageDcm() { - Bitmap currentFrame = this.vsp_Panel.GetCurrentVideoFrame(); + Bitmap currentFrame = player.GetCurrentVideoFrame(); if (currentFrame != null) { //开启区域截图 @@ -1900,6 +2101,634 @@ namespace videoGather } } } + + private void button7_Click(object sender, EventArgs e) + { + + } + + + #region 新的方式内容 + //private void StartCamera() + //{ + + // DsDevice[] devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); + + // if (devices.Length == 0) + // { + // MessageBox.Show("无相机连接!"); + // return; + // } + // else + // { + + // CaptureVideo((DsDevice)comboBox1.SelectedItem); + // } + //} + + + public void CaptureVideo(DsDevice device) + { + pictureBox1.Image = null; + int hr = 0; + IBaseFilter sourceFilter = null; + ISampleGrabber sampleGrabber = null; + + try + { + GetInterfaces(); + hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder); + DsError.ThrowExceptionForHR(hr); + sourceFilter = SelectCaptureDevice(device); + hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture"); + DsError.ThrowExceptionForHR(hr); + sampleGrabber = new SampleGrabber() as ISampleGrabber; + ConfigureSampleGrabber(sampleGrabber); + hr = this.graphBuilder.AddFilter(sampleGrabber as IBaseFilter, "Frame Callback"); + DsError.ThrowExceptionForHR(hr); + SetConfigParams(this.captureGraphBuilder, sourceFilter, _pFPS, _pWidth, _pHeight); + hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, (sampleGrabber as IBaseFilter), null); + + + + DsError.ThrowExceptionForHR(hr); + + SaveSizeInfo(sampleGrabber); + SetupVideoWindow(); + + rot = new DsROTEntry(this.graphBuilder); + + hr = this.mediaControl.Run(); + DsError.ThrowExceptionForHR(hr); + } + catch + { + MessageBox.Show("error!"); + } + finally + { + if (sourceFilter != null) + { + Marshal.ReleaseComObject(sourceFilter); + sourceFilter = null; + } + + if (sampleGrabber != null) + { + Marshal.ReleaseComObject(sampleGrabber); + sampleGrabber = null; + } + } + } + + public void CaptureVideo() + { + pictureBox1.Image = null; + int hr = 0; + IBaseFilter sourceFilter = null; + ISampleGrabber sampleGrabber = null; + + try + { + // Get DirectShow interfaces + GetInterfaces(); + + // Attach the filter graph to the capture graph + hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder); + DsError.ThrowExceptionForHR(hr); + + // Use the system device enumerator and class enumerator to find + // a video capture/preview device, such as a desktop USB video camera. + sourceFilter = FindCaptureDevice(); + // Add Capture filter to graph. + hr = this.graphBuilder.AddFilter(sourceFilter, "Video Capture"); + DsError.ThrowExceptionForHR(hr); + + // Initialize SampleGrabber. + sampleGrabber = new SampleGrabber() as ISampleGrabber; + // Configure SampleGrabber. Add preview callback. + ConfigureSampleGrabber(sampleGrabber); + + // Add SampleGrabber to graph. + hr = this.graphBuilder.AddFilter(sampleGrabber as IBaseFilter, "Frame Callback"); + DsError.ThrowExceptionForHR(hr); + + // Configure preview settings. + SetConfigParams(this.captureGraphBuilder, sourceFilter, _pFPS, _pWidth, _pHeight); + + // Render the preview + hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, (sampleGrabber as IBaseFilter), null); + DsError.ThrowExceptionForHR(hr); + + SaveSizeInfo(sampleGrabber); + + // Set video window style and position + SetupVideoWindow(); + + // Add our graph to the running object table, which will allow + // the GraphEdit application to "spy" on our graph + rot = new DsROTEntry(this.graphBuilder); + + // Start previewing video data + hr = this.mediaControl.Run(); + DsError.ThrowExceptionForHR(hr); + } + catch + { + MessageBox.Show("error!"); + } + finally + { + if (sourceFilter != null) + { + Marshal.ReleaseComObject(sourceFilter); + sourceFilter = null; + } + + if (sampleGrabber != null) + { + Marshal.ReleaseComObject(sampleGrabber); + sampleGrabber = null; + } + } + } + + public IBaseFilter SelectCaptureDevice(DsDevice device) + { + object source = null; + Guid iid = typeof(IBaseFilter).GUID; + device.Mon.BindToObject(null, null, ref iid, out source); + return (IBaseFilter)source; + } + + public IBaseFilter FindCaptureDevice() + { + int hr = 0; +#if USING_NET11 + UCOMIEnumMoniker classEnum = null; + UCOMIMoniker[] moniker = new UCOMIMoniker[1]; +#else + IEnumMoniker classEnum = null; + IMoniker[] moniker = new IMoniker[1]; +#endif + object source = null; + + // Create the system device enumerator + ICreateDevEnum devEnum = (ICreateDevEnum)new CreateDevEnum(); + + // Create an enumerator for the video capture devices + hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0); + DsError.ThrowExceptionForHR(hr); + + // The device enumerator is no more needed + Marshal.ReleaseComObject(devEnum); + + // If there are no enumerators for the requested type, then + // CreateClassEnumerator will succeed, but classEnum will be NULL. + if (classEnum == null) + { + throw new ApplicationException("No video capture device was detected.\r\n\r\n" + + "This sample requires a video capture device, such as a USB WebCam,\r\n" + + "to be installed and working properly. The sample will now close."); + } + + // Use the first video capture device on the device list. + // Note that if the Next() call succeeds but there are no monikers, + // it will return 1 (S_FALSE) (which is not a failure). Therefore, we + // check that the return code is 0 (S_OK). +#if USING_NET11 + int i; + if (classEnum.Next (moniker.Length, moniker, IntPtr.Zero) == 0) +#else + if (classEnum.Next(moniker.Length, moniker, IntPtr.Zero) == 0) +#endif + { + // Bind Moniker to a filter object + Guid iid = typeof(IBaseFilter).GUID; + moniker[0].BindToObject(null, null, ref iid, out source); + } + else + { + throw new ApplicationException("Unable to access video capture device!"); + } + + // Release COM objects + Marshal.ReleaseComObject(moniker[0]); + Marshal.ReleaseComObject(classEnum); + + // An exception is thrown if cast fail + return (IBaseFilter)source; + } + + public void GetInterfaces() + { + int hr = 0; + + // An exception is thrown if cast fail + this.graphBuilder = (IFilterGraph2)new FilterGraph(); + this.captureGraphBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2(); + this.mediaControl = (IMediaControl)this.graphBuilder; + this.videoWindow = (IVideoWindow)this.graphBuilder; + + + DsError.ThrowExceptionForHR(hr); + } + + public void StopCamera() + { + if (mediaControl != null) + { + int hr = mediaControl.StopWhenReady(); + DsError.ThrowExceptionForHR(hr); + } + + if (videoWindow != null) + { + videoWindow.put_Visible(OABool.False); + videoWindow.put_Owner(IntPtr.Zero); + } + + // Remove filter graph from the running object table. + if (rot != null) + { + rot.Dispose(); + rot = null; + } + + if (this.mediaControl != null) + { + Marshal.ReleaseComObject(this.mediaControl); this.mediaControl = null; + Marshal.ReleaseComObject(this.videoWindow); this.videoWindow = null; + Marshal.ReleaseComObject(this.graphBuilder); this.graphBuilder = null; + Marshal.ReleaseComObject(this.captureGraphBuilder); this.captureGraphBuilder = null; + } + } + + public void SetupVideoWindow() + { + int hr = 0; + + // Set the video window to be a child of the PictureBox + hr = this.videoWindow.put_Owner(pictureBox1.Handle); + DsError.ThrowExceptionForHR(hr); + + hr = this.videoWindow.put_WindowStyle(WindowStyle.Child); + DsError.ThrowExceptionForHR(hr); + + // Make the video window visible, now that it is properly positioned + hr = this.videoWindow.put_Visible(OABool.True); + DsError.ThrowExceptionForHR(hr); + + // Set the video position + System.Drawing.Rectangle rc = pictureBox1.ClientRectangle; + hr = videoWindow.SetWindowPosition(0, 0, _pWidth, _pHeight); + DsError.ThrowExceptionForHR(hr); + } + + + + private void SetConfigParams(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int iFrameRate, int iWidth, int iHeight) + { + int hr; + object config; + AMMediaType mediaType; + // Find the stream config interface + hr = capGraph.FindInterface( + PinCategory.Capture, MediaType.Video, capFilter, typeof(IAMStreamConfig).GUID, out config); + + IAMStreamConfig videoStreamConfig = config as IAMStreamConfig; + if (videoStreamConfig == null) + { + throw new Exception("Failed to get IAMStreamConfig"); + } + + // Get the existing format block + hr = videoStreamConfig.GetFormat(out mediaType); + DsError.ThrowExceptionForHR(hr); + + // copy out the videoinfoheader + VideoInfoHeader videoInfoHeader = new VideoInfoHeader(); + Marshal.PtrToStructure(mediaType.formatPtr, videoInfoHeader); + + // if overriding the framerate, set the frame rate + if (iFrameRate > 0) + { + videoInfoHeader.AvgTimePerFrame = 10000000 / iFrameRate; + } + + // if overriding the width, set the width + if (iWidth > 0) + { + videoInfoHeader.BmiHeader.Width = iWidth; + } + + // if overriding the Height, set the Height + if (iHeight > 0) + { + videoInfoHeader.BmiHeader.Height = iHeight; + } + + // Copy the media structure back + Marshal.StructureToPtr(videoInfoHeader, mediaType.formatPtr, false); + + // Set the new format + hr = videoStreamConfig.SetFormat(mediaType); + DsError.ThrowExceptionForHR(hr); + + DsUtils.FreeAMMediaType(mediaType); + mediaType = null; + } + + private void SaveSizeInfo(ISampleGrabber sampleGrabber) + { + int hr; + + // Get the media type from the SampleGrabber + AMMediaType media = new AMMediaType(); + hr = sampleGrabber.GetConnectedMediaType(media); + DsError.ThrowExceptionForHR(hr); + + if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero)) + { + throw new NotSupportedException("Unknown Grabber Media Format"); + } + + // Grab the size info + VideoInfoHeader videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader)); + _pStride = _pWidth * (videoInfoHeader.BmiHeader.BitCount / 8); + + DsUtils.FreeAMMediaType(media); + media = null; + } + + private void ConfigureSampleGrabber(ISampleGrabber sampleGrabber) + { + AMMediaType media; + int hr; + + // Set the media type to Video/RBG24 + media = new AMMediaType(); + media.majorType = MediaType.Video; + media.subType = MediaSubType.RGB24; + media.formatType = FormatType.VideoInfo; + + hr = sampleGrabber.SetMediaType(media); + DsError.ThrowExceptionForHR(hr); + + DsUtils.FreeAMMediaType(media); + media = null; + + hr = sampleGrabber.SetCallback(this, 1); + DsError.ThrowExceptionForHR(hr); + } + + public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen) + { + + if (isGrab) + { + Bitmap bmp = new Bitmap(_pWidth, _pHeight, _pStride, PixelFormat.Format24bppRgb, pBuffer); + bmp.RotateFlip(RotateFlipType.Rotate180FlipX); + this.BeginInvoke((MethodInvoker)delegate + { + // pictureBox2.BackgroundImage = bmp; + var saveName = string.Empty; + if (rowhisinfo != null) + { + saveName = $"{filePath}{$"{rowhisinfo.jianchaid}"}{$"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}"}.jpg"; + } + else + { + + saveName = $"{filePath}{$"{DateTime.Now.ToString("yyyyMMddhhmmssffff")}"}.jpg"; + } + //表示开启了选中区域 截图一下 当背景 + if (isSelecting && overlayPanel.Visible) + { + // 外部设置背景图 + overlayPanel.BackImage = bmp; + } + else + { + #region 根据保存的配置进行裁剪 + // 读取配置,不存在时返回null + var loadedConfig = ConfigHelper.Load("selectionXY"); + // 当存在有效配置时执行裁剪 + if (_isselectionXY=="1" &&loadedConfig != null && loadedConfig.Width > 0 && loadedConfig.Height > 0) + { + // 创建选区矩形 + var cropRect = new System.Drawing.Rectangle( + loadedConfig.X, + loadedConfig.Y, + loadedConfig.Width, + loadedConfig.Height); + + // 执行裁剪(使用Clone保证原图不被释放) + using (bmp) // 释放原始位图 + { + // 创建新Bitmap对象保存裁剪结果 + var croppedBmp = bmp.Clone(cropRect, bmp.PixelFormat); + croppedBmp.Save(saveName); + } + } + else // 无有效配置时保存完整图片 + { + bmp.Save(saveName); + } + #endregion + + // bmp.Save(saveName); + imagearray.Add(saveName); + // 使用 Invoke 方法确保在UI线程上更新标签的文本 + if (label1.InvokeRequired) + { + label1.Invoke(new Action(() => label1.Text = imagearray.Count.ToString())); + } + else + { + label1.Text = imagearray.Count.ToString(); + } + label1.Refresh(); + } + + + }); + isGrab = false; + } + return 0; + } + + public int SampleCB(double SampleTime, IMediaSample pSample) + { + return 0; + } + #endregion + + /// + /// 点击"选区域"按钮 + /// + /// + /// + private void button7_Click_1(object sender, EventArgs e) + { + // 清空选区 + selectionRect = System.Drawing.Rectangle.Empty; + //截图 + btn_Capture_Click(null,null); + // 显示遮罩层 + overlayPanel.Visible = true; + + // 进入选区模式 + isSelecting = true; + } + + + + #region 鼠标事件处理 + // 鼠标按下事件处理 + private void Overlay_MouseDown(object sender, MouseEventArgs e) + { + // 当处于选区模式且按下左键时 + if (isSelecting && e.Button == MouseButtons.Left) + { + // 记录起始坐标点 + startPoint = e.Location; + // 初始化选区矩形 + selectionRect = new System.Drawing.Rectangle(e.Location, Size.Empty); + } + } + + // 鼠标移动事件处理 + private void Overlay_MouseMove(object sender, MouseEventArgs e) + { + + // 当处于选区模式且按住左键拖动时 + if (isSelecting && e.Button == MouseButtons.Left) + { + // 计算选区矩形参数 + int x = Math.Min(startPoint.X, e.X); + int y = Math.Min(startPoint.Y, e.Y); + int width = Math.Abs(e.X - startPoint.X); + int height = Math.Abs(e.Y - startPoint.Y); + + // 更新选区矩形 + selectionRect = new System.Drawing.Rectangle(x, y, width, height); + + // 限制选区在控件范围内 + selectionRect = System.Drawing.Rectangle.Intersect(selectionRect, overlayPanel.ClientRectangle); + + // 触发重绘更新选区显示 + overlayPanel.Invalidate(); + } + } + + // 鼠标释放事件处理 + private void Overlay_MouseUp(object sender, MouseEventArgs e) + { + // 当结束选区操作时 + if (isSelecting) + { + // 退出选区模式 + isSelecting = false; + // 隐藏遮罩层 + overlayPanel.Visible = false; + // 清除背景图 + overlayPanel.BackImage = null; + // 转换到实际图像坐标系 + System.Drawing.Rectangle imageCoords = GetImageCoordinates(selectionRect); + var config = new SelectionConfig + { + X = imageCoords.X, + Y = imageCoords.Y, + Width = imageCoords.Width, + Height = imageCoords.Height + }; + //存储坐标 + ConfigHelper.Save("selectionXY", config); + //// 显示坐标信息 + //MessageBox.Show($"捕获的选区坐标:\n" + + // $"X={imageCoords.X}, Y={imageCoords.Y}\n" + + // $"宽={imageCoords.Width}, 高={imageCoords.Height}"); + } + } + #endregion + + + #region 绘制逻辑 + private void pictureBox1_Paint(object sender, PaintEventArgs e) + { + //if (!selectionRect.IsEmpty) + //{ + // using (Pen pen = new Pen(Color.Blue, 2)) // 蓝色边框 + // { + // e.Graphics.DrawRectangle(pen, selectionRect); + // } + //} + } + + // 遮罩层绘制事件 + private void Overlay_Paint(object sender, PaintEventArgs e) + { + // 当存在有效选区时进行绘制 + if (!selectionRect.IsEmpty) + { + using (Pen pen = new Pen(Color.Red, 2)) // 2像素宽红色边框 + using (Brush brush = new SolidBrush(Color.FromArgb(50, 255, 0, 0))) // 半透明红色填充 + { + // 绘制半透明填充区域 + e.Graphics.FillRectangle(brush, selectionRect); + // 绘制红色边框 + e.Graphics.DrawRectangle(pen, selectionRect); + } + } + } + #endregion + + #region 坐标转换方法 + /// + /// 将控件坐标转换为实际图像坐标 + /// + /// 控件坐标系中的矩形 + /// 实际图像坐标系中的矩形 + private System.Drawing.Rectangle GetImageCoordinates(System.Drawing.Rectangle controlRect) + { + if (pictureBox1.SizeMode == PictureBoxSizeMode.Zoom) + { + // 检查图像是否存在 + if (pictureBox1.Image == null) + return System.Drawing.Rectangle.Empty; + + // 计算Zoom模式的缩放比例 + float zoomRatio = Math.Min( + (float)pictureBox1.Width / pictureBox1.Image.Width, + (float)pictureBox1.Height / pictureBox1.Image.Height); + + // 计算实际显示区域参数 + int displayWidth = (int)(pictureBox1.Image.Width * zoomRatio); + int displayHeight = (int)(pictureBox1.Image.Height * zoomRatio); + int offsetX = (pictureBox1.Width - displayWidth) / 2; + int offsetY = (pictureBox1.Height - displayHeight) / 2; + + // 执行坐标转换(考虑居中显示偏移) + return new System.Drawing.Rectangle( + (int)((controlRect.X - offsetX) / zoomRatio), + (int)((controlRect.Y - offsetY) / zoomRatio), + (int)(controlRect.Width / zoomRatio), + (int)(controlRect.Height / zoomRatio)); + } + else + { + // 如果SizeMode不是Zoom,直接返回 + return controlRect; + } + } + #endregion + + private void button8_Click(object sender, EventArgs e) + { + // 重新绘制选区 + pictureBox1.Invalidate(); + } } } diff --git a/videoGather.csproj b/videoGather.csproj index b0081ab..0806d7c 100644 --- a/videoGather.csproj +++ b/videoGather.csproj @@ -86,6 +86,10 @@ packages\fo-dicom.Desktop.4.0.0\lib\net45\Dicom.Core.dll + + False + bin\x86\Debug\DirectShowLib.dll + packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll @@ -104,6 +108,10 @@ + + + Component + @@ -129,6 +137,7 @@ +