using HalconDotNet;
|
using Newtonsoft.Json;
|
using Newtonsoft.Json.Serialization;
|
using OpenCvSharp;
|
using OpenCvSharp.Extensions;
|
using System;
|
using System.Collections.Concurrent;
|
using System.Diagnostics;
|
using System.Text;
|
|
namespace LB_VisionProcesses.Cameras
|
{
|
[Process("相机取图", Category = "取像工具", Description = "创建相机取图工具")]
|
public class CameraConfig : IProcess
|
{
|
/// <summary>
|
/// 相机集合(Key:相机SN,Value:相机句柄)
|
/// </summary>
|
public ConcurrentDictionary<string, BaseCamera> dicCameras { get; set; }
|
|
public BaseCamera Camera { get; set; }
|
|
public CameraConfig(ConcurrentDictionary<string, BaseCamera> dicCameras = null)
|
{
|
this.dicCameras = dicCameras;
|
|
strProcessName = "相机取图";
|
strProcessClass = "LB_VisionProcesses.CameraConfig.CommunicatorConfig";
|
|
Params.Inputs.Add("相机SN", "");
|
Params.Inputs.Add("触发模式", TriggerMode.On);
|
Params.Inputs.Add("触发方式", TriggerSource.Software);
|
Params.Inputs.Add("触发极性", TriggerPolarity.RisingEdge);
|
Params.Inputs.Add("触发消抖", 0);
|
Params.Inputs.Add("触发延时", 0);
|
Params.Inputs.Add("曝光时间", 1000);
|
Params.Inputs.Add("增益", 1);
|
Params.Inputs.Add("是否每次写入参数", false);
|
Params.Inputs.Add("是否本地取图", false);
|
Params.Inputs.Add("本地取图路径", "");
|
Params.Inputs.Add("本地取图序号", 0);
|
|
Params.Inputs.Add("超时时间", 1000);
|
Params.Inputs.Add("是否失败重新取图", false);
|
|
// 2D轮胎计数配置(默认为1,表示3D模式;2D相机设置为n)
|
Params.Inputs.Add("ImagesPerTyre", 1);
|
Params.Inputs.Add("当前轮胎ID", 1);
|
Params.Inputs.Add("当前图像序号", 0);
|
}
|
|
/// <summary>
|
/// 加载算法
|
/// </summary>
|
/// <param name="fullPath">完整路径带.json</param>
|
/// <returns></returns>
|
public override bool Load(string fullPath = null)
|
{
|
try
|
{
|
if (string.IsNullOrEmpty(fullPath))
|
return false;
|
|
if (!fullPath.Contains(".json"))
|
{
|
Debug.WriteLine("文件路径不完整");
|
return false;
|
}
|
if (string.IsNullOrEmpty(fullPath) || fullPath.Trim() == "")
|
{
|
Debug.WriteLine("文件路径不完整");
|
return false;
|
}
|
|
// 获取不带文件名的目录路径
|
string directoryPath = Path.GetDirectoryName(fullPath);
|
strProcessName = Path.GetFileNameWithoutExtension(fullPath);
|
|
if (!File.Exists(fullPath))
|
{
|
Debug.WriteLine("文件不存在创建空文件");
|
Save(directoryPath);
|
return true;
|
}
|
|
string strJson = string.Empty;
|
using (StreamReader streamReader = new StreamReader(fullPath, Encoding.UTF8))
|
{
|
strJson = streamReader.ReadToEnd();
|
streamReader.Close();
|
}
|
Params = JsonConvert.DeserializeObject<ProcessParams>(strJson);
|
if (Params == null)
|
return false;
|
Params.FixDeserializedData();
|
|
#region 加载后把参数写入到相机中
|
bool isLocal = Convert.ToBoolean(Params.Inputs["是否本地取图"].ToString());
|
if (!isLocal)
|
{
|
string SN = Params.Inputs["相机SN"].ToString();
|
|
if (dicCameras == null || !dicCameras.ContainsKey(SN))
|
return false;
|
|
BaseCamera BaseCamera = dicCameras[SN];
|
if (BaseCamera == null)
|
return false;
|
|
if (!BaseCamera.isGrabbing)
|
{
|
if (!BaseCamera.StartGrabbing())
|
return false;
|
}
|
BaseCamera.SetCamConfig(this);
|
}
|
#endregion
|
|
return true;
|
}
|
catch { return false; }
|
}
|
|
/// <summary>
|
/// 保存算法
|
/// </summary>
|
/// <param name="filePath">不带.json</param>
|
/// <returns></returns>
|
public override bool Save(string filePath = null)
|
{
|
try
|
{
|
if (string.IsNullOrEmpty(filePath) || filePath.Trim() == "")
|
{
|
Debug.WriteLine("文件路径不完整");
|
return false;
|
}
|
|
string strJson = string.Empty;
|
var settings = new JsonSerializerSettings
|
{
|
Formatting = Newtonsoft.Json.Formatting.Indented,
|
// 自定义缩进(4空格)
|
ContractResolver = new DefaultContractResolver
|
{
|
NamingStrategy = new CamelCaseNamingStrategy()
|
}
|
};
|
strJson = JsonConvert.SerializeObject(Params, settings);
|
|
Params = JsonConvert.DeserializeObject<ProcessParams>(strJson);
|
if (Params == null)
|
return false;
|
|
//判断文件夹是否存在,防呆输入为文件名称
|
if (!Directory.Exists(filePath))
|
{
|
try
|
{
|
Directory.CreateDirectory(filePath);
|
}
|
catch (Exception)
|
{ }
|
}
|
|
File.WriteAllText(filePath + "//" + strProcessName + ".json", strJson, Encoding.UTF8);
|
return true;
|
}
|
catch { return false; }
|
}
|
|
public override bool Run()
|
{
|
DateTime StartTime = DateTime.Now;
|
try
|
{
|
InitRunParams();
|
string SN = string.Empty;
|
bool isLocal = Convert.ToBoolean(Params.Inputs["是否本地取图"]?.ToString());
|
bool isUpParams = Convert.ToBoolean(Params.Inputs["是否每次写入参数"]?.ToString());
|
bool isRegrab = Convert.ToBoolean(Params.Inputs["是否失败重新取图"]?.ToString());
|
int timeout = Convert.ToInt16(Params.Inputs["超时时间"]?.ToString());
|
|
if (isLocal)
|
{
|
if (Params.Inputs["本地取图路径"] is List<string>)
|
{
|
List<string> lstPaths = Params.Inputs["本地取图路径"] as List<string>;
|
if (lstPaths != null && int.TryParse(Params.Inputs["本地取图序号"]?.ToString(), out int indexPath)
|
&& lstPaths.Count > indexPath)
|
{
|
SN = lstPaths[indexPath];
|
|
if (lstPaths.Count > indexPath + 1)
|
Params.Inputs["本地取图序号"] = indexPath + 1;
|
else
|
Params.Inputs["本地取图序号"] = 0;
|
|
Mat src = Cv2.ImRead(SN);
|
if (src != null && !src.Empty())
|
OutputImage = src.ToBitmap();
|
}
|
}
|
}
|
else
|
{
|
SN = Params.Inputs["相机SN"].ToString();
|
|
if (dicCameras == null || !dicCameras.ContainsKey(SN))
|
{
|
Msg = $"相机[{SN}]不存在";
|
Result = false;
|
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
|
return Result;
|
}
|
|
// 始终检查缓存的Camera对象是否与全局字典中的一致
|
// 防止相机被删除并重建后(SN相同), 仍然引用旧的已销毁对象
|
if (dicCameras.ContainsKey(SN))
|
{
|
var currentCam = dicCameras[SN];
|
if (Camera != currentCam)
|
{
|
Camera = currentCam;
|
// 如果切换了相机实例,且未开始采集,尝试开启采集
|
if (Camera != null && !Camera.isGrabbing)
|
{
|
try { Camera.StartGrabbing(); } catch { }
|
}
|
}
|
}
|
else
|
{
|
Camera = null;
|
}
|
|
if (Camera == null)
|
{
|
Msg = $"相机[{SN}]未实例化";
|
Result = false;
|
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
|
return Result;
|
}
|
|
CameraConfig oriConfig = new CameraConfig();
|
|
if (isUpParams)
|
{
|
Camera.GetCamConfig(out oriConfig);
|
Camera.SetCamConfig(this);
|
}
|
|
if (!Enum.TryParse(Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource))
|
{
|
Msg = $"相机[{SN}]设置的触发方式不正确,值为:{Params.Inputs["触发方式"].ToString()}";
|
Result = false;
|
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
|
return Result;
|
}
|
|
Bitmap bitmap = null;
|
int times = 5;
|
do
|
{
|
times--;
|
if (TriggerSource == TriggerSource.Software)
|
{
|
Camera.GetImageWithSoftTrigger(out bitmap, timeout);
|
}
|
else
|
{
|
Camera.GetImage(out bitmap, timeout);
|
}
|
if (bitmap != null)
|
{
|
break;
|
}
|
else if (isRegrab)
|
{
|
Debug.WriteLine($"取图失败,重新取图,剩余次数:{times}");
|
}
|
} while (times > 0 && isRegrab);
|
|
if (isUpParams)
|
{
|
Camera.SetCamConfig(oriConfig);
|
}
|
OutputImage = bitmap;
|
}
|
|
if (OutputImage == null)
|
{
|
Msg = $"相机[{SN}]获取图像失败";
|
Result = false;
|
}
|
}
|
catch (Exception ex)
|
{
|
Msg = ex.Message;
|
Result = false;
|
}
|
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
|
return Result;
|
}
|
|
public override void InitRunParams()
|
{
|
Result = true;
|
Msg = "";
|
if (OutputImage != null)
|
{
|
if (OutputImage is HObject)
|
((HObject)OutputImage).Dispose();
|
else if (OutputImage is Mat)
|
((Mat)OutputImage).Dispose();
|
else if (OutputImage is Bitmap)
|
((Bitmap)OutputImage).Dispose();
|
|
OutputImage = null;
|
}
|
if (Record != null)
|
{
|
Record.Dispose();
|
Record = null;
|
}
|
}
|
|
public override void Dispose()
|
{
|
if (InputImage != null)
|
{
|
if (InputImage is HObject)
|
((HObject)InputImage).Dispose();
|
else if (InputImage is Mat)
|
((Mat)InputImage).Dispose();
|
else if (InputImage is Bitmap)
|
((Bitmap)InputImage).Dispose();
|
|
InputImage = null;
|
}
|
|
if (OutputImage != null)
|
{
|
if (OutputImage is HObject)
|
((HObject)OutputImage).Dispose();
|
else if (OutputImage is Mat)
|
((Mat)OutputImage).Dispose();
|
else if (OutputImage is Bitmap)
|
((Bitmap)OutputImage).Dispose();
|
|
OutputImage = null;
|
}
|
return;
|
}
|
|
public override object Clone()
|
{
|
try
|
{
|
var obj = (CameraConfig)MemberwiseClone(); // 浅拷贝
|
|
// 手动处理引用类型
|
if (OutputImage != null)
|
{
|
if (OutputImage is Bitmap)
|
{
|
obj.OutputImage = ((Bitmap)OutputImage).Clone();
|
}
|
}
|
|
return obj;
|
}
|
catch { return (CameraConfig)MemberwiseClone(); }
|
}
|
}
|
}
|