using HalconDotNet; using LB_VisionControl; using MVSDK_Net; using Newtonsoft.Json.Linq; using OpenCvSharp; using System; using System.Collections.Concurrent; using System.Data; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Text; using System.Windows.Forms; using System.Xml.Linq; namespace LB_VisionProcesses.Cameras { public partial class CameraForm : Form { Bitmap Bitmap = null; BaseCamera camera { get; set; } CameraConfig camConfig { get; set; } ConcurrentDictionary dicCameras { get; set; } string fullPath = string.Empty; public CameraForm() { InitializeComponent(); } public CameraForm(ConcurrentDictionary dicCameras, CameraConfig camConfig, string fullPath) { InitializeComponent(); //传入相机句柄后禁用连接/断开按键 this.btnOpen.Enabled = false; this.btnClose.Enabled = false; this.cmbBrand.Enabled = false; this.panel_Picture.Controls.Clear(); this.dicCameras = dicCameras; this.fullPath = fullPath; this.camConfig = camConfig; foreach (var CamSN in dicCameras.Keys) cmbSN.Items.Add(CamSN); Enum.TryParse(camConfig.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource); if (TriggerSource == TriggerSource.Software) radioButtonSoft.Checked = true; else radioButtonSoft.Checked = false; this.Text = camConfig.strProcessName; txtExp.Text = camConfig.Params.Inputs["曝光时间"].ToString(); txtGain.Text = camConfig.Params.Inputs["增益"].ToString(); ckbLocalTest.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否本地取图"].ToString()); if (camConfig.Params.Inputs["本地取图路径"] is List) { List lstPaths = camConfig.Params.Inputs["本地取图路径"] as List; if (lstPaths != null && lstPaths.Count > 0) { foreach (var path in lstPaths) cmbImagesPath.Items.Add(path); cmbImagesPath.Text = lstPaths[0]; } } } UserPictureBox onlinePictureBox { get; set; } private System.Windows.Forms.Timer updateTimer; Total total = new Total { iImageCount = 0, iScanCount = 0 }; DateTime startGrabtime = DateTime.Now; private void CameraForm_Load(object sender, EventArgs e) { // 设置一个定时器,每 100 毫秒触发一次 updateTimer = new System.Windows.Forms.Timer(); updateTimer.Interval = 1000; // 设置定时器间隔为 1000 毫秒 updateTimer.Tick += (sender, e) => UpdateUI(); updateTimer.Start(); onlinePictureBox = new UserPictureBox(panel_Picture); this.panel_Picture.Controls.Clear(); this.panel_Picture.Controls.Add(onlinePictureBox); onlinePictureBox.Dock = DockStyle.Fill; // 使用 LINQ 获取所有枚举值并打印 var brands = Enum.GetValues(typeof(CameraBrand)).Cast(); foreach (var brand in brands) { cmbBrand.Items.Add(brand.ToString()); } //选择Cam会触发ValueChanged事件,没有输入相机的情况下选择到-1 if (camConfig != null) { string SN = camConfig.Params.Inputs["相机SN"].ToString(); int Index = cmbSN.FindString(SN); cmbSN.Text = SN; cmbSN.SelectedIndex = Index; ckbLocalTest.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否本地取图"].ToString()); ckbUpParams.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否每次写入参数"].ToString()); ckbRegrab.Checked = Convert.ToBoolean(camConfig.Params.Inputs["是否失败重新取图"].ToString()); txtTimeout.Text = camConfig.Params.Inputs["超时时间"].ToString(); if (camConfig.OutputImage != null && camConfig.OutputImage is Bitmap bitmap) onlinePictureBox.Image = bitmap; lblCapTime.Text = $"{camConfig.RunTime}ms"; } } private void UpdateUI() { try { // 如果当前不是 UI 线程,则通过 Invoke 将操作调度到 UI 线程 if (this.InvokeRequired) { this.Invoke(new Action(() => { double fps = total.iImageCount / (DateTime.Now - startGrabtime).TotalSeconds; this.lblPicCount.Text = total.iImageCount.ToString(); })); } else { // 如果已经在 UI 线程上,直接更新 double fps = total.iImageCount / (DateTime.Now - startGrabtime).TotalSeconds; this.lblPicCount.Text = total.iImageCount.ToString(); } } catch { } } private void cmbSN_MouseDown(object sender, MouseEventArgs e) { if (dicCameras != null && dicCameras.Count > 0) return; cmbSN.Items.Clear(); // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { // 使用 switch 语句来判断枚举值 switch (brand) { case CameraBrand.HRCamera: camera = new HRCamera(); break; case CameraBrand.LBCamera: //camera = new LBCamera(); break; default: Debug.WriteLine("未知品牌"); break; } foreach (var item in camera.GetListEnum()) { if (!cmbSN.Items.Contains(item)) cmbSN.Items.Add(item); } } else { Debug.WriteLine("无效的枚举值!"); } } private void cmbSN_SelectedIndexChanged(object sender, EventArgs e) { btnEdit.Enabled = false; //camConfig不为空(输入有相机),更改相机选择需要重新加载刷图事件 if (camConfig != null && camera != null) { camera.ImageGrabbed -= GetImageBllComplete; } //输入无camConfig,但是该相机已经打开过了需要释放原相机 else if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.Dispose(); } if (dicCameras != null && dicCameras.Count > 0 && dicCameras.ContainsKey(cmbSN.Text)) camera = dicCameras[cmbSN.Text]; //说明相机已经初始化成功 if (camera != null && camera.isGrabbing) { camera.ImageGrabbed -= GetImageBllComplete; camera.ImageGrabbed += GetImageBllComplete; int Index = cmbBrand.FindString(camera.Brand.ToString()); ; cmbBrand.Text = camera.Brand.ToString(); cmbBrand.SelectedIndex = Index; } } private void btnOpen_Click(object sender, EventArgs e) { // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.Dispose(); } // 使用 switch 语句来判断枚举值 switch (brand) { case CameraBrand.LBCamera: //camera = new LBCamera(); break; case CameraBrand.HRCamera: camera = new HRCamera(); break; default: Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】未知品牌"); return; } if (cmbSN.Items.Count > 0 && camera.InitDevice(cmbSN.Text.ToString(), this.Handle)) { camera.ImageGrabbed -= GetImageBllComplete; camera.ImageGrabbed += GetImageBllComplete; MessageBox.Show(camera.SN + "打开成功"); } } else { Debug.WriteLine($"【{DateTime.Now:HH:mm:ss.fff}】无效的枚举值!"); } if (camera != null) { this.BeginInvoke(new Action(() => { double exp = 0; double gain = 0; camera.GetExpouseTime(out exp); txtExp.Text = exp.ToString(); camera.GetGain(out gain); txtGain.Text = gain.ToString(); camera.GetTriggerMode(out TriggerMode mode, out TriggerSource source); if (source != TriggerSource.Software) radioButtonHard.Checked = true; else radioButtonSoft.Checked = true; })); } } private void btnClose_Click(object sender, EventArgs e) { if (camera == null) return; this.btnEdit.Enabled = false; if (camera.CloseDevice()) { MessageBox.Show(camera.SN + "断开成功"); this.panel_Picture.Controls.Clear(); } } private void btnEdit_Click(object sender, EventArgs e) { if (camera == null) return; } /// /// 相机回调运行 /// /// /// private void GetImageBllComplete(object sender, CameraEventArgs e) { if (e.Bitmap == null) return; lock (e.Bitmap) { if (this.InvokeRequired) // 检查是否需要在UI线程上调用 { this.Invoke(new Action(() => { onlinePictureBox.Image = e.Bitmap; })); // 递归调用自身,但这次在UI线程上 } else { onlinePictureBox.Image = e.Bitmap; } } total.iImageCount++; try { Bitmap = e.Bitmap.Clone() as Bitmap; } catch { } } private ulong ImageWidth; private ulong ImageHeight; private Pen pen = new Pen(Color.Blue, 3f); private System.Drawing.Point[] stPointList = new System.Drawing.Point[4]; private void btnGrabOnce_Click(object sender, EventArgs e) { DateTime StartTime = DateTime.Now; if (camera == null) return; Task.Factory.StartNew(() => { //camera.GetCamConfig(out CameraConfig OriCamConfig); camera.SetExpouseTime(Convert.ToDouble(txtExp.Text)); camera.SetGain(Convert.ToDouble(txtGain.Text)); camera.GetImageWithSoftTrigger(out Bitmap bitmap); this.BeginInvoke(new Action(() => { if (bitmap != null) this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; else this.lblCapTime.Text = "-1ms"; })); //复原原通讯口设置 //camera.SetCamConfig(OriCamConfig); }); } private void btnStartHard_Click(object sender, EventArgs e) { if (camera == null) return; total.Clear(); // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { camera.StopGrabbing(); camera.StartWith_HardTriggerModel(); } startGrabtime = DateTime.Now; cmbSN.Enabled = false; cmbBrand.Enabled = false; } private void btnStartGrab_Click(object sender, EventArgs e) { if (camera == null) return; total.Clear(); // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { Task.Factory.StartNew(() => { camera.StopGrabbing(); camera.StartWith_SoftTriggerModel(); }); } startGrabtime = DateTime.Now; cmbSN.Enabled = false; cmbBrand.Enabled = false; } private void btnCloseGrab_Click(object sender, EventArgs e) { try { if (camera == null) return; // 尝试将输入字符串转换为枚举值 if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand)) { Task.Factory.StartNew(() => { camera.StopGrabbing(); camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); camera.StartGrabbing(); }); } cmbSN.Enabled = true; cmbBrand.Enabled = true; } catch { } } private void btnLocalGrab_Click(object sender, EventArgs e) { try { if (cmbImagesPath.Items.Count <= 0) return; DateTime StartTime = DateTime.Now; int index = cmbImagesPath.SelectedIndex; if (cmbImagesPath.Items.Count > index + 1) cmbImagesPath.Text = cmbImagesPath.Items[index + 1].ToString(); else cmbImagesPath.Text = cmbImagesPath.Items[0].ToString(); using (Mat src = Cv2.ImRead(cmbImagesPath.Text)) { if (src != null && !src.Empty()) { using (Bitmap bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(src)) { this.Invoke(new Action(() => { this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms"; })); onlinePictureBox.Image = bitmap; } } } } catch { } } private void txtExp_TextChanged(object sender, EventArgs e) { if (camera == null) return; double exp = 1000; if (double.TryParse(txtExp.Text.ToString(), out exp)) camera.SetExpouseTime(exp); } private void txtGain_TextChanged(object sender, EventArgs e) { if (camera == null) return; double gain = 10; if (double.TryParse(txtGain.Text.ToString(), out gain)) camera.SetGain(gain); } private void btnAddImages_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); // 设置文件对话框的属性 openFileDialog.Multiselect = true; // 不允许多选 // 设置文件过滤器,支持多种文件类型 openFileDialog.Filter = "Image Files (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|All Files (*.*)|*.*"; // 显示文件对话框 DialogResult result = openFileDialog.ShowDialog(); // 处理对话框返回结果 if (result == DialogResult.OK) { cmbImagesPath.Items.Clear(); // 获取用户选择的文件名 string[] selectedFiles = openFileDialog.FileNames; // 将文件路径添加到队列中 foreach (string file in selectedFiles) cmbImagesPath.Items.Add(file); if (selectedFiles.Length > 0) cmbImagesPath.Text = selectedFiles[0]; } } private void CameraForm_FormClosing(object sender, FormClosingEventArgs e) { //路径为空说明无需保存 if (string.IsNullOrEmpty(fullPath)) { e.Cancel = false; //确认关闭窗体 return; } DialogResult res = MessageBox.Show("是否保存?", "提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); //保存结果信息 /// 参数1:显示文本,参数2:标题,参数3:按键类型,参数4:显示图标 if (res == DialogResult.Cancel) //取消 { e.Cancel = true; //取消关闭窗体 return; } if (res == DialogResult.Yes) //保存VPP { camConfig.Params.Inputs.Add("相机SN", cmbSN.Text); camConfig.Params.Inputs.Add("触发模式", TriggerMode.On); camConfig.Params.Inputs.Add("触发方式", radioButtonSoft.Checked ? TriggerSource.Software : TriggerSource.Line0); camConfig.Params.Inputs.Add("是否本地取图", ckbLocalTest.Checked); camConfig.Params.Inputs.Add("本地取图路径", cmbImagesPath.Items.Cast().ToList()); camConfig.Params.Inputs.Add("是否失败重新取图", ckbRegrab.Checked); if (int.TryParse(txtTimeout.Text, out int timeout)) camConfig.Params.Inputs.Add("超时时间", timeout); if (float.TryParse(txtExp.Text, out float exp)) camConfig.Params.Inputs.Add("曝光时间", exp); if (float.TryParse(txtGain.Text, out float gain)) camConfig.Params.Inputs.Add("增益", gain); string filePath = Path.GetDirectoryName(fullPath); camConfig.Save(filePath); e.Cancel = false; //确认关闭窗体 return; } } private void CameraForm_FormClosed(object sender, FormClosedEventArgs e) { this.onlinePictureBox.Image = null; if (camera != null) { camera.ImageGrabbed -= GetImageBllComplete; camera.StopGrabbing(); if (radioButtonSoft.Checked) camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); else camera.SetTriggerMode(TriggerMode.On, TriggerSource.Line0); camera.StartGrabbing(); } //路径为空说明为测试模式,需要释放相机 if (string.IsNullOrEmpty(fullPath) && camera != null) camera.Dispose(); } private ImageFormat GetImageFormat(string extension) { return extension.ToLower() switch { ".jpg" or ".jpeg" => ImageFormat.Jpeg, ".png" => ImageFormat.Png, ".bmp" => ImageFormat.Bmp, ".gif" => ImageFormat.Gif, ".tiff" => ImageFormat.Tiff, _ => ImageFormat.Png // 默认格式 }; } private void btnSaveImage_Click(object sender, EventArgs e) { if (Bitmap == null) { MessageBox.Show("没有图像可保存!", "异常"); return; } // 创建 SaveFileDialog 实例 using (SaveFileDialog saveFileDialog = new SaveFileDialog()) { // 设置对话框标题 saveFileDialog.Title = "保存文件"; // 设置默认路径 saveFileDialog.InitialDirectory = Application.StartupPath; // 设置文件过滤器,支持多种文件类型 saveFileDialog.Filter = "Image Files (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp|All Files (*.*)|*.*"; // 设置默认文件名 saveFileDialog.FileName = "output.jpg"; // 显示对话框并检查用户是否点击了保存按钮 if (saveFileDialog.ShowDialog() == DialogResult.OK) { // 获取用户选择的文件路径 string fullPath = saveFileDialog.FileName; Debug.WriteLine("选择的文件路径是: " + fullPath); if (!string.IsNullOrEmpty(fullPath)) { Bitmap.Save(fullPath, GetImageFormat(Path.GetExtension(fullPath))); } } } } private void radioButtonSoft_CheckedChanged(object sender, EventArgs e) { btnGrabOnce.Enabled = radioButtonSoft.Checked; btnStartGrab.Enabled = radioButtonSoft.Checked; btnCloseGrab.Enabled = radioButtonSoft.Checked; btnStartHard.Enabled = !radioButtonSoft.Checked; if (camera == null) return; if (radioButtonSoft.Checked) camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software); else camera.SetTriggerMode(TriggerMode.On, TriggerSource.Line0); } System.Windows.Forms.ToolTip ToolTip = new System.Windows.Forms.ToolTip(); private void cmbImagesPath_MouseHover(object sender, EventArgs e) { if (cmbImagesPath.SelectedIndex != -1 && camConfig != null && int.TryParse(camConfig.Params.Inputs["本地取图序号"]?.ToString(), out int indexPath)) { // 显示ToolTip(相对于控件的位置) ToolTip.Show($"当前索引序号:{indexPath}", (Control)sender, ((Control)sender).Width / 2, // x偏移 -20, // y偏移(负值表示上方) 2000); // 显示时间(ms) } } private void cmbImagesPath_SelectedIndexChanged(object sender, EventArgs e) { if (camConfig != null) camConfig.Params.Inputs["本地取图序号"] = cmbImagesPath.SelectedIndex; } private void controlBox_2DCameraForm_Click(object sender, EventArgs e) { } private void dungeonControlBox_2DCameraForm_Click(object sender, EventArgs e) { // this.Close(); } private void theme_2DCameraForm_Click(object sender, EventArgs e) { } } }