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<string, BaseCamera> dicCameras { get; set; }
|
|
string fullPath = string.Empty;
|
|
public CameraForm()
|
{
|
InitializeComponent();
|
}
|
|
public CameraForm(ConcurrentDictionary<string, BaseCamera> 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<string>)
|
{
|
List<string> lstPaths = camConfig.Params.Inputs["本地取图路径"] as List<string>;
|
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<CameraBrand>();
|
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;
|
}
|
|
/// <summary>
|
/// 相机回调运行
|
/// </summary>
|
/// <param name="CCDName"></param>
|
/// <param name="image"></param>
|
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<string>().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)
|
{
|
|
}
|
}
|
}
|