修改框架(未完全完成)实现单个相机分开绑定算法
This commit is contained in:
@@ -25,6 +25,8 @@ namespace Check.Main.Infer
|
||||
/// </summary>
|
||||
private static int _enabledCameraCount = 0;
|
||||
|
||||
private static long _productCounter = 0; // 新增产品计数器10.22
|
||||
|
||||
public static event EventHandler<DetectionResultEventArgs> OnDetectionCompleted;
|
||||
public static bool IsDetectionRunning { get; private set; } = false;
|
||||
|
||||
@@ -51,72 +53,220 @@ namespace Check.Main.Infer
|
||||
public static void Initialize(List<CameraSettings> cameraSettings, List<ModelSettings> modelSettings)
|
||||
{
|
||||
Shutdown(); // 先关闭旧的
|
||||
YoloModelManager.Initialize(modelSettings); // 确保 YOLO 模型在初始化协调器时加载。10.22新增
|
||||
|
||||
var enabledCameras = cameraSettings.Where(c => c.IsEnabled).ToList();
|
||||
_enabledCameraCount = enabledCameras.Count;
|
||||
if (_enabledCameraCount == 0) return;
|
||||
//if (_enabledCameraCount == 0) return;
|
||||
if (_enabledCameraCount == 0)
|
||||
{
|
||||
ThreadSafeLogger.Log("没有启用的相机,检测协调器未初始化。");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var camSetting in enabledCameras)
|
||||
{
|
||||
// 找到与相机编号匹配的模型
|
||||
var model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID);
|
||||
ModelSettings model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID);
|
||||
if (model == null)
|
||||
{
|
||||
ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像。");
|
||||
//ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像");
|
||||
ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 匹配的模型 (ID: {camSetting.ModelID})。该相机将无法处理图像。");
|
||||
continue;
|
||||
}
|
||||
|
||||
var processor = new CameraProcessor(camSetting.CameraIndex,camSetting.ModelID);
|
||||
IDetector detector = null;
|
||||
object detectorSettings = null; // 用于传递特定检测器的设置
|
||||
|
||||
// 根据相机的 CheckType 和模型的 AlgorithmType 决定使用哪个检测器
|
||||
if (camSetting.CheckType == CheckType.Traditional && model.M_AType == AlgorithmType.Tradition)
|
||||
{
|
||||
detector = new HalconTemplateDetector();
|
||||
detectorSettings = new HalconDetectionSettings { ScoreThreshold = model.HalconScoreThreshold };
|
||||
ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 HALCON 传统算法。");
|
||||
}
|
||||
else if (camSetting.CheckType == CheckType.DeepLearning && model.M_AType == AlgorithmType.DeepLearning)
|
||||
{
|
||||
detector = new YoloDetector();
|
||||
detectorSettings = new YoloDetectionSettings
|
||||
{
|
||||
ConfidenceThreshold = model.YoloConfidenceThreshold,
|
||||
NmsThreshold = model.YoloNmsThreshold
|
||||
};
|
||||
ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 YOLO 深度学习算法。");
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadSafeLogger.Log($"[警告] 相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 的 CheckType ({camSetting.CheckType}) 与模型 (ID: {model.Id}, AlgorithmType: {model.M_AType}) 不匹配或不支持。跳过此相机。");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 初始化检测器
|
||||
try
|
||||
{
|
||||
// 对于YOLO,modelPath实际上传递的是ModelID
|
||||
// 对于HALCON,modelPath是实际的模板目录
|
||||
string initPath = (detector is YoloDetector) ? model.Id.ToString() : model.Path;
|
||||
detector.Initialize(initPath, detectorSettings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ThreadSafeLogger.Log($"[错误] 初始化相机 #{camSetting.CameraIndex} 的检测器失败: {ex.Message}");
|
||||
detector?.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
var processor = new CameraProcessor(camSetting.CameraIndex, detector, model);
|
||||
_processors.TryAdd(camSetting.CameraIndex, processor);
|
||||
processor.Start();
|
||||
}
|
||||
ThreadSafeLogger.Log($"检测协调器已初始化,启动了 {_processors.Count} 个相机处理线程。");
|
||||
}
|
||||
|
||||
//public static void EnqueueImage(int cameraIndex, Bitmap bmp)
|
||||
//{
|
||||
// if (_processors.TryGetValue(cameraIndex, out var processor))
|
||||
// {
|
||||
// processor.EnqueueImage(bmp);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 如果找不到处理器,必须释放Bitmap防止泄漏
|
||||
// bmp?.Dispose();
|
||||
// }
|
||||
//}
|
||||
|
||||
public static void EnqueueImage(int cameraIndex, Bitmap bmp)
|
||||
{
|
||||
// 在图像进入队列之前生成一个新的产品ID
|
||||
long currentProductId;
|
||||
lock (_productAssemblies) // 同步访问产品计数器
|
||||
{
|
||||
_productCounter++;
|
||||
currentProductId = _productCounter;
|
||||
}
|
||||
|
||||
if (_processors.TryGetValue(cameraIndex, out var processor))
|
||||
{
|
||||
processor.EnqueueImage(bmp);
|
||||
processor.EnqueueImage(bmp, currentProductId); // 传递产品ID
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果找不到处理器,必须释放Bitmap防止泄漏
|
||||
bmp?.Dispose();
|
||||
bmp?.Dispose(); // 如果找不到处理器,必须释放Bitmap防止泄漏
|
||||
ThreadSafeLogger.Log($"[警告] 未能为相机 {cameraIndex} 找到处理器,产品 {currentProductId} 的图像被丢弃。");
|
||||
// 如果没有处理器,不需要在 _productAssemblies 中添加,因为不会有结果返回
|
||||
}
|
||||
}
|
||||
|
||||
// 供 CameraProcessor 回调,用以组装产品
|
||||
public static void AssembleProduct(ImageData data, string result)
|
||||
{
|
||||
var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount));
|
||||
//// 供 CameraProcessor 回调,用以组装产品
|
||||
//public static void AssembleProduct(ImageData data, string result)
|
||||
//{
|
||||
// var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount));
|
||||
|
||||
if (assembly.AddResult(data.CameraIndex, result))
|
||||
// if (assembly.AddResult(data.CameraIndex, result))
|
||||
// {
|
||||
// string finalResult = assembly.GetFinalResult();
|
||||
// ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}");
|
||||
|
||||
// // 只有在检测运行时,才触发事件
|
||||
// if (IsDetectionRunning)
|
||||
// {
|
||||
// OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK"));
|
||||
// }
|
||||
|
||||
// if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly))
|
||||
// {
|
||||
// finishedAssembly.Dispose();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// CameraProcessor 回调,用以组装产品
|
||||
public static void AssembleProduct(long productId, int cameraIndex, bool isOk, Bitmap resultImage)
|
||||
{
|
||||
// GetOrAdd 确保 ProductAssembly 只被创建一次
|
||||
var assembly = _productAssemblies.GetOrAdd(productId, (id) => new ProductAssembly(id, _enabledCameraCount));
|
||||
|
||||
assembly.AddResult(cameraIndex, isOk, resultImage);
|
||||
|
||||
// 检查产品是否已完成所有相机的检测
|
||||
if (assembly.IsComplete())
|
||||
{
|
||||
string finalResult = assembly.GetFinalResult();
|
||||
string finalResult = assembly.GetFinalResult() ? "OK" : "NG";
|
||||
ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}");
|
||||
|
||||
// 只有在检测运行时,才触发事件
|
||||
// 触发事件 (例如更新主UI上的总OK/NG计数)
|
||||
if (IsDetectionRunning)
|
||||
{
|
||||
OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK"));
|
||||
OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(assembly.GetFinalResult()));
|
||||
}
|
||||
|
||||
if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly))
|
||||
// PLC 写入逻辑
|
||||
if (FrmMain.PlcClient != null) // 假设 FrmMain.PlcClient 可访问
|
||||
{
|
||||
finishedAssembly.Dispose();
|
||||
try
|
||||
{
|
||||
if (assembly.GetFinalResult()) // 最终结果 OK
|
||||
{
|
||||
FrmMain.PlcClient.WriteBool("M90", true); // 写入M90为1
|
||||
Thread.Sleep(50); // 短暂延时
|
||||
FrmMain.PlcClient.WriteBool("M90", false);
|
||||
}
|
||||
else // 最终结果 NG
|
||||
{
|
||||
FrmMain.PlcClient.WriteBool("M91", true); // 写入M91为1
|
||||
Thread.Sleep(50); // 短暂延时
|
||||
FrmMain.PlcClient.WriteBool("M91", false);
|
||||
}
|
||||
ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 最终结果 {finalResult} 已写入PLC。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ThreadSafeLogger.Log($"[错误] 写入PLC失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThreadSafeLogger.Log($"[警告] 产品 #{assembly.ProductId} 检测结果未能写入PLC:PLC客户端未连接。");
|
||||
}
|
||||
|
||||
// 移除并释放 ProductAssembly
|
||||
if (_productAssemblies.TryRemove(productId, out var finishedAssembly))
|
||||
{
|
||||
finishedAssembly.Dispose(); // 释放所有存储的 Bitmap
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 命令所有活动的相机处理器重置它们的内部计数器。
|
||||
/// </summary>
|
||||
|
||||
///// <summary>
|
||||
///// 命令所有活动的相机处理器重置它们的内部计数器。
|
||||
///// </summary>
|
||||
//public static void ResetAllCounters()
|
||||
//{
|
||||
// foreach (var processor in _processors.Values)
|
||||
// {
|
||||
// processor.ResetCounter();
|
||||
// }
|
||||
// ThreadSafeLogger.Log("所有相机处理器的产品计数器已重置。");
|
||||
//}
|
||||
|
||||
public static void ResetAllCounters()
|
||||
{
|
||||
lock (_productAssemblies)
|
||||
{
|
||||
_productCounter = 0;
|
||||
// 清空所有未完成的产品,并释放其资源
|
||||
foreach (var assembly in _productAssemblies.Values)
|
||||
{
|
||||
assembly.Dispose();
|
||||
}
|
||||
_productAssemblies.Clear();
|
||||
}
|
||||
foreach (var processor in _processors.Values)
|
||||
{
|
||||
processor.ResetCounter();
|
||||
}
|
||||
ThreadSafeLogger.Log("所有相机处理器的产品计数器已重置。");
|
||||
ThreadSafeLogger.Log("所有相机处理器和产品计数器已重置。");
|
||||
}
|
||||
|
||||
public static CameraProcessor GetProcessor(int cameraIndex)
|
||||
@@ -142,8 +292,88 @@ namespace Check.Main.Infer
|
||||
{
|
||||
assembly.Dispose();
|
||||
}
|
||||
_productAssemblies.Clear();
|
||||
YoloModelManager.Shutdown(); // 确保YOLO模型也关闭
|
||||
ThreadSafeLogger.Log("检测协调器已关闭。");
|
||||
}
|
||||
}
|
||||
|
||||
// 新增 ProductAssembly 类,用于集中管理一个产品的检测结果和图像
|
||||
public class ProductAssembly : IDisposable
|
||||
{
|
||||
public long ProductId { get; }
|
||||
private readonly int _expectedCameraCount;
|
||||
private readonly ConcurrentDictionary<int, bool> _cameraResults = new ConcurrentDictionary<int, bool>();
|
||||
private readonly ConcurrentDictionary<int, Bitmap> _resultImages = new ConcurrentDictionary<int, Bitmap>(); // 存储每个相机的结果图像
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public ProductAssembly(long productId, int expectedCameraCount)
|
||||
{
|
||||
ProductId = productId;
|
||||
_expectedCameraCount = expectedCameraCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加单个相机的检测结果。
|
||||
/// </summary>
|
||||
/// <param name="cameraIndex">相机编号。</param>
|
||||
/// <param name="isOk">检测结果是否为OK。</param>
|
||||
/// <param name="resultImage">带有检测结果的图像。</param>
|
||||
public void AddResult(int cameraIndex, bool isOk, Bitmap resultImage)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_cameraResults.TryAdd(cameraIndex, isOk);
|
||||
if (resultImage != null)
|
||||
{
|
||||
// 克隆图像,确保 ProductAssembly 拥有其所有权
|
||||
_resultImages.TryAdd(cameraIndex, (Bitmap)resultImage.Clone());
|
||||
resultImage.Dispose(); // 释放传入的原始图像副本
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否所有相机都已提交结果。
|
||||
/// </summary>
|
||||
public bool IsComplete()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _cameraResults.Count == _expectedCameraCount;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最终产品检测结果(所有相机都OK才为OK)。
|
||||
/// </summary>
|
||||
public bool GetFinalResult()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _cameraResults.Values.All(r => r);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个相机的结果图像。
|
||||
/// </summary>
|
||||
public Bitmap GetResultImage(int cameraIndex)
|
||||
{
|
||||
_resultImages.TryGetValue(cameraIndex, out var bmp);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
foreach (var bmp in _resultImages.Values)
|
||||
{
|
||||
bmp?.Dispose();
|
||||
}
|
||||
_resultImages.Clear();
|
||||
_cameraResults.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
Check.Main/Infer/HalconTemplateDetector.cs
Normal file
57
Check.Main/Infer/HalconTemplateDetector.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using HalconTemplateMatch;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Check.Main.Infer
|
||||
{
|
||||
public class HalconTemplateDetector : IDetector
|
||||
{
|
||||
private LogoMatcher _matcher; // 使用您现有的 LogoMatcher
|
||||
private double _scoreThreshold = 0.5; // 可以从 ModelSettings 中配置
|
||||
|
||||
public void Initialize(string modelPath, object detectionSettings = null)
|
||||
{
|
||||
_matcher = new LogoMatcher();
|
||||
_matcher.LoadTemplates(modelPath); // modelPath 现在是 Halcon 模板目录
|
||||
|
||||
// 如果 detectionSettings 包含阈值,可以在这里解析
|
||||
if (detectionSettings is HalconDetectionSettings halconSettings)
|
||||
{
|
||||
_scoreThreshold = halconSettings.ScoreThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
public DetectionResult Detect(Bitmap image)
|
||||
{
|
||||
if (_matcher == null)
|
||||
throw new InvalidOperationException("HalconTemplateDetector 未初始化。");
|
||||
|
||||
Bitmap resultImage;
|
||||
double score = _matcher.FindLogo(image, out resultImage);
|
||||
bool isOk = score >= _scoreThreshold;
|
||||
string message = isOk ? "OK" : "NG";
|
||||
|
||||
// 如果需要绘制结果图像,可以在 LogoMatcher 中添加绘制逻辑并返回
|
||||
// 假设 LogoMatcher 也可以返回一个带有匹配标记的 Bitmap
|
||||
// Bitmap resultImage = _matcher.DrawResults(image, score);
|
||||
|
||||
return new DetectionResult(isOk, message, score); //, resultImage
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// LogoMatcher 如果有需要释放的资源,可以在这里处理
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助设置类,用于传递给 HalconTemplateDetector
|
||||
public class HalconDetectionSettings
|
||||
{
|
||||
public double ScoreThreshold { get; set; } = 0.5;
|
||||
// 可以添加其他 Halcon 匹配参数
|
||||
}
|
||||
}
|
||||
62
Check.Main/Infer/IDetector.cs
Normal file
62
Check.Main/Infer/IDetector.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Check.Main.Infer
|
||||
{
|
||||
public interface IDetector : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化检测器。
|
||||
/// </summary>
|
||||
/// <param name="modelPath">模型文件路径(对于传统算法可能是模板目录)</param>
|
||||
/// <param name="detectionSettings">特定于检测器的设置对象(可选,可以用于传递阈值等)</param>
|
||||
void Initialize(string modelPath, object detectionSettings = null);
|
||||
|
||||
/// <summary>
|
||||
/// 执行图像检测。
|
||||
/// </summary>
|
||||
/// <param name="image">待检测的图像。</param>
|
||||
/// <returns>包含检测结果(如OK/NG,得分,边界框等)的统一对象。</returns>
|
||||
DetectionResult Detect(Bitmap image);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统一的检测结果类。
|
||||
/// </summary>
|
||||
public class DetectionResult
|
||||
{
|
||||
public bool IsOk { get; set; }
|
||||
public string Message { get; set; }
|
||||
public double Score { get; set; }
|
||||
public List<RectangleF> BoundingBoxes { get; set; } // 深度学习可能返回多个目标框
|
||||
|
||||
// 如果需要,可以添加带有绘制结果的图像
|
||||
public Bitmap ResultImage { get; set; }
|
||||
|
||||
public DetectionResult(bool isOk, string message = "Unknown", double score = 0.0, List<RectangleF> boundingBoxes = null, Bitmap resultImage = null)
|
||||
{
|
||||
IsOk = isOk;
|
||||
Message = message;
|
||||
Score = score;
|
||||
BoundingBoxes = boundingBoxes ?? new List<RectangleF>();
|
||||
ResultImage = resultImage;
|
||||
}
|
||||
}
|
||||
|
||||
//// 辅助设置类,用于传递给 HalconTemplateDetector
|
||||
//public class HalconDetectionSettings
|
||||
//{
|
||||
// public double ScoreThreshold { get; set; } = 0.5;
|
||||
//}
|
||||
|
||||
// 辅助设置类,用于传递给 YoloDetector
|
||||
public class YoloDetectionSettings
|
||||
{
|
||||
public float ConfidenceThreshold { get; set; } = 0.25f;
|
||||
public float NmsThreshold { get; set; } = 0.45f;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,14 @@
|
||||
using Check.Main.Common;
|
||||
//using Check.Main.Common;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.ComponentModel;// 需要引入此命名空间以使用 INotifyPropertyChanged
|
||||
//using System.Linq;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Runtime.Serialization;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
|
||||
using Check.Main.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
@@ -7,28 +17,12 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms.Design;
|
||||
using System.Drawing.Design;
|
||||
|
||||
namespace Check.Main.Infer
|
||||
{
|
||||
public enum DetectDevice
|
||||
{
|
||||
[Description("CPU")]
|
||||
CPU = 0,
|
||||
|
||||
[Description("GPU")]
|
||||
GPU,
|
||||
|
||||
//[Description("VPU")]
|
||||
//VPU,
|
||||
}
|
||||
public enum AlgorithmType
|
||||
{
|
||||
[Description("传统算法")]
|
||||
Tradition = 0,
|
||||
|
||||
[Description("深度学习")]
|
||||
DeepLearning,
|
||||
}
|
||||
public enum CheckModelType
|
||||
{
|
||||
[Description("分类")]
|
||||
@@ -47,9 +41,32 @@ namespace Check.Main.Infer
|
||||
PoseEstimation
|
||||
}
|
||||
|
||||
[Serializable] // 确保可被XML序列化
|
||||
public class ModelSettings : INotifyPropertyChanged, ICloneable
|
||||
public enum AlgorithmType
|
||||
{
|
||||
[Description("传统算法")]
|
||||
Tradition = 0,
|
||||
|
||||
[Description("深度学习")]
|
||||
DeepLearning,
|
||||
}
|
||||
|
||||
public enum DetectDevice
|
||||
{
|
||||
[Description("CPU")]
|
||||
CPU = 0,
|
||||
|
||||
[Description("GPU")]
|
||||
GPU,
|
||||
|
||||
//[Description("VPU")]
|
||||
//VPU,
|
||||
}
|
||||
|
||||
|
||||
[Serializable] // 确保可被XML序列化
|
||||
public class ModelSettings : INotifyPropertyChanged, ICloneable//类ModelSettings继承或实现两个接口:①提供一个事件,用于在属性值更改时发出通知;②提供一个方法,用于创建对象的独立副本
|
||||
{
|
||||
//1. 实现 INotifyPropertyChanged 接口
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private int _id;
|
||||
@@ -57,9 +74,14 @@ namespace Check.Main.Infer
|
||||
private string _path = "";
|
||||
private DetectDevice _checkDevice=DetectDevice.CPU;
|
||||
private AlgorithmType _mAType = AlgorithmType.Tradition;
|
||||
private CheckModelType _mType = CheckModelType.Classification;
|
||||
private CheckModelType _mType = CheckModelType.ObjectDetection;
|
||||
private bool _isEnabled = true;
|
||||
|
||||
// 新增 HALCON 和 YOLO 的参数。10.22
|
||||
private double _halconScoreThreshold = 0.5;
|
||||
private float _yoloConfidenceThreshold = 0.25f;
|
||||
private float _yoloNmsThreshold = 0.45f;
|
||||
|
||||
[Category("基本信息"), DisplayName("模型编号"), Description("模型的唯一标识符,用于与相机编号对应。")]
|
||||
public int Id
|
||||
{
|
||||
@@ -73,13 +95,6 @@ namespace Check.Main.Infer
|
||||
get => _name;
|
||||
set { if (_name != value) { _name = value; OnPropertyChanged(); } }
|
||||
}
|
||||
[Category("基本信息"), DisplayName("推理设备"), Description("推理模型的设备。")]
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
public DetectDevice CheckDevice
|
||||
{
|
||||
get => _checkDevice;
|
||||
set { if (_checkDevice != value) { _checkDevice = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
[Category("基本信息"), DisplayName("算法类型"), Description("所使用的算法的类型。")]
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
@@ -88,6 +103,16 @@ namespace Check.Main.Infer
|
||||
get => _mAType;
|
||||
set { if (_mAType != value) { _mAType = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
[Category("基本信息"), DisplayName("推理设备"), Description("推理模型的设备。")]
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
public DetectDevice CheckDevice
|
||||
{
|
||||
get => _checkDevice;
|
||||
set { if (_checkDevice != value) { _checkDevice = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
|
||||
[Category("基本信息"), DisplayName("模型类型"), Description("推理模型的类型。")]
|
||||
[TypeConverter(typeof(EnumDescriptionTypeConverter))]
|
||||
public CheckModelType MType
|
||||
@@ -96,18 +121,18 @@ namespace Check.Main.Infer
|
||||
set { if (_mType != value) { _mType = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
|
||||
[Category("基本信息"), DisplayName("是否启用"), Description("是否在程序启动时是否启用模型")]
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
set {
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Category("文件"), DisplayName("模型路径"), Description("选择模型文件(.onnx, .bin, etc., .pt)。")]
|
||||
@@ -118,6 +143,27 @@ namespace Check.Main.Infer
|
||||
set { if (_path != value) { _path = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
[Category("模型参数"), DisplayName("HALCON得分阈值"), Description("HALCON模板匹配的得分阈值(0-1)。")]
|
||||
public double HalconScoreThreshold
|
||||
{
|
||||
get => _halconScoreThreshold;
|
||||
set { if (_halconScoreThreshold != value) { _halconScoreThreshold = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
[Category("模型参数"), DisplayName("YOLO置信度阈值"), Description("YOLO检测的置信度阈值(0-1)。")]
|
||||
public float YoloConfidenceThreshold
|
||||
{
|
||||
get => _yoloConfidenceThreshold;
|
||||
set { if (_yoloConfidenceThreshold != value) { _yoloConfidenceThreshold = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
[Category("模型参数"), DisplayName("YOLO NMS阈值"), Description("YOLO检测的非极大值抑制(NMS)阈值(0-1)。")]
|
||||
public float YoloNmsThreshold
|
||||
{
|
||||
get => _yoloNmsThreshold;
|
||||
set { if (_yoloNmsThreshold != value) { _yoloNmsThreshold = value; OnPropertyChanged(); } }
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
409
Check.Main/Infer/YoloDetector.cs
Normal file
409
Check.Main/Infer/YoloDetector.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using Check.Main.Camera;
|
||||
using Check.Main.Common;
|
||||
using OpenCvSharp;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using YoloDotNet;
|
||||
using YoloDotNet.Models;
|
||||
|
||||
namespace Check.Main.Infer
|
||||
{
|
||||
/// <summary>
|
||||
/// YOLO 检测结果对象,包含标签、置信度与检测框
|
||||
/// </summary>
|
||||
public class YoloPrediction
|
||||
{
|
||||
public YoloLabel Label { get; set; }
|
||||
public float Score { get; set; }
|
||||
public BoundingBox BoundingBox { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// YOLO 类别标签
|
||||
/// </summary>
|
||||
public class YoloLabel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测框坐标结构体
|
||||
/// </summary>
|
||||
public struct BoundingBox
|
||||
{
|
||||
public float Left { get; set; }
|
||||
public float Top { get; set; }
|
||||
public float Width { get; set; }
|
||||
public float Height { get; set; }
|
||||
|
||||
public BoundingBox(float left, float top, float width, float height)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// YOLO 目标检测器实现类
|
||||
/// </summary>
|
||||
public class YoloDetector : IDetector, IDisposable
|
||||
{
|
||||
private Yolo _yoloModel;
|
||||
private int _modelID;
|
||||
private float _confidenceThreshold = 0.25f;
|
||||
private float _nmsThreshold = 0.45f;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 YOLO 检测器并加载模型
|
||||
/// </summary>
|
||||
/// <param name="modelIdStr">模型 ID 字符串</param>
|
||||
/// <param name="detectionSettings">可选检测参数</param>
|
||||
/// <exception cref="ArgumentException">ID 无效</exception>
|
||||
/// <exception cref="InvalidOperationException">模型未加载</exception>
|
||||
public void Initialize(string modelIdStr, object detectionSettings = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(modelIdStr))
|
||||
throw new ArgumentException("模型ID字符串不能为空。", nameof(modelIdStr));
|
||||
|
||||
if (!int.TryParse(modelIdStr, out _modelID))
|
||||
throw new ArgumentException("模型ID必须为有效整数。", nameof(modelIdStr));
|
||||
|
||||
_yoloModel = YoloModelManager.GetModel(_modelID)
|
||||
?? throw new InvalidOperationException($"YOLO 模型 (ID: {_modelID}) 未加载或找不到。");
|
||||
|
||||
if (detectionSettings is YoloDetectionSettings yoloSettings)
|
||||
{
|
||||
_confidenceThreshold = Math.Clamp(yoloSettings.ConfidenceThreshold, 0f, 1f);
|
||||
_nmsThreshold = Math.Clamp(yoloSettings.NmsThreshold, 0f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行检测,判断是否含有 logo 类对象
|
||||
/// </summary>
|
||||
public DetectionResult Detect(Bitmap image)
|
||||
{
|
||||
if (_yoloModel == null)
|
||||
throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
|
||||
|
||||
if (image == null)
|
||||
return new DetectionResult(false, "输入图像为空。");
|
||||
|
||||
try
|
||||
{
|
||||
using var skBitmap = CameraProcessor.ToSKBitmapFast(image);
|
||||
if (skBitmap == null)
|
||||
return new DetectionResult(false, "图像转换失败。");
|
||||
|
||||
using var skImage = SKImage.FromBitmap(skBitmap);
|
||||
if (skImage == null)
|
||||
return new DetectionResult(false, "无法生成 SKImage。");
|
||||
|
||||
var predictions = _yoloModel.RunObjectDetection(
|
||||
skImage,
|
||||
confidence: _confidenceThreshold,
|
||||
iou: _nmsThreshold
|
||||
);
|
||||
|
||||
// 检查是否检测到 logo
|
||||
bool foundLogo = predictions.Any(p =>
|
||||
p.Label.Name.Equals("logo", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return foundLogo
|
||||
? new DetectionResult(false, "NG")
|
||||
: new DetectionResult(true, "OK");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new DetectionResult(false, $"检测失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在图像上绘制检测框与标签
|
||||
/// </summary>
|
||||
public Bitmap DrawYoloPredictions(Bitmap source, IEnumerable<YoloPrediction> predictions)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
|
||||
if (predictions == null || !predictions.Any())
|
||||
return (Bitmap)source.Clone();
|
||||
|
||||
Bitmap output = (Bitmap)source.Clone();
|
||||
|
||||
using var graphics = Graphics.FromImage(output);
|
||||
using var pen = new Pen(Color.Yellow, 2);
|
||||
using var font = new Font("Arial", 10, FontStyle.Bold);
|
||||
using var brush = new SolidBrush(Color.Yellow);
|
||||
|
||||
foreach (var pred in predictions)
|
||||
{
|
||||
var box = pred.BoundingBox;
|
||||
var rect = new RectangleF(box.Left, box.Top, box.Width, box.Height);
|
||||
|
||||
// 绘制检测框
|
||||
graphics.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
|
||||
|
||||
// 绘制标签
|
||||
string label = $"{pred.Label?.Name ?? "unknown"} ({pred.Score:P1})";
|
||||
graphics.DrawString(label, font, brush, rect.X, rect.Y - 15);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无需显式释放模型,资源由 YoloModelManager 管理
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
//_yoloModel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
//总结,区分好YoloModelManager.cs和YoloDetector.cs各自的职能,谁负责模型管理,谁负责yolo算法的执行,现在这两个文件是交织在一起比较乱的,有时间去处理一下
|
||||
//看上面的定义的一些结构体胡总和类,就可以调用啦
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//using Check.Main.Camera;
|
||||
//using Check.Main.Common;
|
||||
//using SkiaSharp;
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Drawing;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
//using YoloDotNet;
|
||||
//using YoloDotNet.Models;
|
||||
|
||||
|
||||
|
||||
//namespace Check.Main.Infer
|
||||
//{
|
||||
// public class YoloDetector : IDetector
|
||||
// {
|
||||
// private Yolo _yoloModel;
|
||||
// private int _modelID; // 需要知道模型ID来从 YoloModelManager 获取
|
||||
// private float _confidenceThreshold = 0.25f;
|
||||
// private float _nmsThreshold = 0.45f;
|
||||
|
||||
// public void Initialize(string modelIdStr, object detectionSettings = null)
|
||||
// {
|
||||
// if (!int.TryParse(modelIdStr, out _modelID))
|
||||
// {
|
||||
// throw new ArgumentException("YoloDetector 初始化需要有效的模型ID字符串。", nameof(modelIdStr));
|
||||
// }
|
||||
// _yoloModel = YoloModelManager.GetModel(_modelID);
|
||||
// if (_yoloModel == null)
|
||||
// {
|
||||
// throw new InvalidOperationException($"YOLO 模型 (ID: {_modelID}) 未加载或找不到。");
|
||||
// }
|
||||
|
||||
// if (detectionSettings is YoloDetectionSettings yoloSettings)
|
||||
// {
|
||||
// _confidenceThreshold = yoloSettings.ConfidenceThreshold;
|
||||
// _nmsThreshold = yoloSettings.NmsThreshold;
|
||||
// // 注意:YOLO模型的置信度和NMS阈值最好在YoloModelManager加载时设置
|
||||
// // 这里如果需要运行时调整,可能需要Yolo.SetThresholds方法
|
||||
// }
|
||||
// }
|
||||
|
||||
// public DetectionResult Detect(Bitmap image)
|
||||
// {
|
||||
// if (_yoloModel == null)
|
||||
// throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
|
||||
|
||||
// using (var skImage = CameraProcessor.ToSKBitmapFast(image)) // 使用 CameraProcessor 的静态方法
|
||||
// {
|
||||
// if (skImage == null)
|
||||
// {
|
||||
// return new DetectionResult(false, "图像转换失败");
|
||||
// }
|
||||
|
||||
// // 在这里可以应用运行时阈值,如果Yolo模型支持
|
||||
// // _yoloModel.Confidence = _confidenceThreshold;
|
||||
// // _yoloModel.Nms = _nmsThreshold;
|
||||
|
||||
// var predictions = _yoloModel.RunObjectDetection(skImage);
|
||||
|
||||
// bool isOk = !predictions.Any(); // 假设没有检测到任何目标为 OK
|
||||
// string message = isOk ? "OK" : "NG";
|
||||
|
||||
// List<RectangleF> boundingBoxes = predictions.Select(p => new RectangleF(p.Rectangle.X, p.Rectangle.Y, p.Rectangle.Width, p.Rectangle.Height)).ToList();
|
||||
// Bitmap resultImage = DrawYoloPredictions(image, predictions);
|
||||
|
||||
// return new DetectionResult(isOk, message, 0, boundingBoxes, resultImage);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
|
||||
// {
|
||||
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
|
||||
// using (Graphics g = Graphics.FromImage(resultBmp))
|
||||
// {
|
||||
// Pen ngPen = new Pen(Color.Red, 3);
|
||||
// Font font = new Font("Arial", 12, FontStyle.Bold);
|
||||
// Brush brush = new SolidBrush(Color.Red);
|
||||
|
||||
// foreach (var p in predictions)
|
||||
// {
|
||||
// Rectangle rect = new Rectangle((int)p.Rectangle.X, (int)p.Rectangle.Y, (int)p.Rectangle.Width, (int)p.Rectangle.Height);
|
||||
// g.DrawRectangle(ngPen, rect);
|
||||
// g.DrawString($"{p.Label} ({p.Confidence:P})", font, brush, rect.X, rect.Y - 20);
|
||||
// }
|
||||
// }
|
||||
// return resultBmp;
|
||||
// }
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// // YOLO 模型生命周期由 YoloModelManager 管理,这里不需要额外释放
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//public DetectionResult Detect(Bitmap image)
|
||||
//{
|
||||
// if (_yoloModel == null)
|
||||
// throw new InvalidOperationException("YoloDetector 未初始化或模型未加载。");
|
||||
|
||||
// using (var skBitmap = CameraProcessor.ToSKBitmapFast(image))
|
||||
// using (var skImage = SKImage.FromBitmap(skBitmap))
|
||||
// {
|
||||
// //注意深拷贝浅拷贝的概念,复制的图片别忘了释放
|
||||
// var output = image.Clone();
|
||||
|
||||
// if (skImage == null)
|
||||
// return new DetectionResult(false, "图像转换失败");
|
||||
|
||||
// //var results = _yoloModel.RunObjectDetection(skImage, confidence: 0.4f, iou: 0.5f);
|
||||
|
||||
// var predictions = _yoloModel.RunObjectDetection(
|
||||
// skImage,
|
||||
// confidence: _confidenceThreshold,
|
||||
// iou: _nmsThreshold
|
||||
// );
|
||||
// var ExistBool = predictions.FirstOrDefault(res => res.Label.Name.Equals("logo", StringComparison.OrdinalIgnoreCase));
|
||||
// if (ExistBool != null)
|
||||
// {
|
||||
// var box = ExistBool.BoundingBox;
|
||||
// var rect = new Rect(box.Left, box.Top, box.Width, box.Height);
|
||||
|
||||
// //Cv2.Rectangle(output, rect, Scalar.Yellow, 2);
|
||||
// //Cv2.PutText(output, $"{logoResult.Label.Name}: {logoResult.Confidence:P2}", new CvPoint(rect.X, rect.Y - 10), HersheyFonts.HersheySimplex, 0.6, Scalar.Yellow, 2);
|
||||
// }
|
||||
// bool isOk = !predictions.Any(); // 没有检测结果为OK
|
||||
// string message = isOk ? "OK" : "NG";
|
||||
|
||||
// //var boundingBoxes = predictions
|
||||
// // .Select(p => new RectangleF(p.Rectangle.X, p.Rectangle.Y, p.Rectangle.Width, p.Rectangle.Height))
|
||||
// // .ToList();
|
||||
|
||||
// //Bitmap resultImage = DrawYoloPredictions(image, predictions);
|
||||
|
||||
// //return new DetectionResult(isOk, message, 0, boundingBoxes, resultImage);
|
||||
// }
|
||||
//}
|
||||
|
||||
//// 注意:如果你使用的是 YoloResult,请改为 IEnumerable<YoloResult> IEnumerable<YoloPrediction>
|
||||
//private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
|
||||
//{
|
||||
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
|
||||
// using (Graphics g = Graphics.FromImage(resultBmp))
|
||||
// {
|
||||
// Pen boxPen = new Pen(Color.Red, 3);
|
||||
// Font font = new Font("Arial", 12, FontStyle.Bold);
|
||||
// Brush brush = new SolidBrush(Color.Red);
|
||||
|
||||
// foreach (var p in predictions)
|
||||
// {
|
||||
// Rectangle rect = new Rectangle(
|
||||
// (int)p.Rectangle.X,
|
||||
// (int)p.Rectangle.Y,
|
||||
// (int)p.Rectangle.Width,
|
||||
// (int)p.Rectangle.Height
|
||||
// );
|
||||
// g.DrawRectangle(boxPen, rect);
|
||||
// g.DrawString($"{p.Label} ({p.Confidence:P0})", font, brush, rect.X, rect.Y - 20);
|
||||
// }
|
||||
|
||||
// boxPen.Dispose();
|
||||
// font.Dispose();
|
||||
// brush.Dispose();
|
||||
// }
|
||||
// return resultBmp;
|
||||
//}
|
||||
// 错误②:CS0246 解决方法:确保 YoloPrediction 的完整命名空间正确引用。
|
||||
// 由于你的文件开头已经有了 `using YoloDotNet.Models;`
|
||||
// 所以这里直接使用 `YoloPrediction` 应该是正确的,除非 `YoloPrediction` 不在该命名空间下。
|
||||
// 如果问题依然存在,检查 YoloDotNet.Models 命名空间中 YoloPrediction 的具体定义。
|
||||
//private Bitmap DrawYoloPredictions(Bitmap originalImage, IEnumerable<YoloPrediction> predictions)
|
||||
//{
|
||||
// Bitmap resultBmp = (Bitmap)originalImage.Clone();
|
||||
// using (Graphics g = Graphics.FromImage(resultBmp))
|
||||
// {
|
||||
// Pen boxPen = new Pen(Color.Red, 3);
|
||||
// Font font = new Font("Arial", 12, FontStyle.Bold);
|
||||
// Brush brush = new SolidBrush(Color.Red);
|
||||
|
||||
// foreach (var p in predictions)
|
||||
// {
|
||||
// Rectangle rect = new Rectangle(
|
||||
// (int)p.Rectangle.X,
|
||||
// (int)p.Rectangle.Y,
|
||||
// (int)p.Rectangle.Width,
|
||||
// (int)p.Rectangle.Height
|
||||
// );
|
||||
// g.DrawRectangle(boxPen, rect);
|
||||
// g.DrawString($"{p.Label} ({p.Confidence:P0})", font, brush, rect.X, rect.Y - 20);
|
||||
// }
|
||||
|
||||
// boxPen.Dispose();
|
||||
// font.Dispose();
|
||||
// brush.Dispose();
|
||||
// }
|
||||
// return resultBmp;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -31,40 +31,41 @@ namespace Check.Main.Infer
|
||||
if (modelSettings == null) return;
|
||||
|
||||
ThreadSafeLogger.Log("开始加载YOLO模型...");
|
||||
foreach (var setting in modelSettings)
|
||||
// 筛选出启用的深度学习模型进行加载
|
||||
foreach (var setting in modelSettings.Where(s => s.IsEnabled && s.M_AType == AlgorithmType.DeepLearning))
|
||||
{
|
||||
bool gpuUse = false;
|
||||
if (setting.CheckDevice == DetectDevice.GPU)
|
||||
{
|
||||
gpuUse = true;
|
||||
}
|
||||
bool gpuUse = setting.CheckDevice == DetectDevice.GPU;
|
||||
|
||||
if (string.IsNullOrEmpty(setting.Path) || !File.Exists(setting.Path))
|
||||
{
|
||||
ThreadSafeLogger.Log($"[警告] 模型 '{setting.Name}' (ID: {setting.Id}) 路径无效或文件不存在,已跳过加载。");
|
||||
ThreadSafeLogger.Log($"[警告] YOLO模型 '{setting.Name}' (ID: {setting.Id}) 路径无效或文件不存在,已跳过加载。");
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 创建YOLO实例
|
||||
var yolo = new Yolo(new YoloOptions
|
||||
{
|
||||
OnnxModel = setting.Path,
|
||||
// 您可以根据需要从配置中读取这些值
|
||||
ModelType = (YoloDotNet.Enums.ModelType)setting.MType,
|
||||
Cuda = gpuUse, // 推荐使用GPU
|
||||
PrimeGpu = false
|
||||
Cuda = gpuUse,
|
||||
//Confidence = setting.YoloConfidenceThreshold, // 从 ModelSettings 读取
|
||||
//Nms = setting.YoloNmsThreshold, // 从 ModelSettings 读取
|
||||
PrimeGpu = false // 根据需求设置
|
||||
});
|
||||
|
||||
// 保存阈值配置
|
||||
var conf = setting.YoloConfidenceThreshold;
|
||||
var nms = setting.YoloNmsThreshold;
|
||||
|
||||
if (_loadedModels.TryAdd(setting.Id, yolo))
|
||||
{
|
||||
ThreadSafeLogger.Log($"成功加载模型 '{setting.Name}' (ID: {setting.Id})。");
|
||||
ThreadSafeLogger.Log($"成功加载YOLO模型 '{setting.Name}' (ID: {setting.Id})。");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ThreadSafeLogger.Log($"[错误] 加载模型 '{setting.Name}' (ID: {setting.Id}) 失败: {ex.Message}");
|
||||
ThreadSafeLogger.Log($"[错误] 加载YOLO模型 '{setting.Name}' (ID: {setting.Id}) 失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
ThreadSafeLogger.Log($"YOLO模型加载完成,共成功加载 {_loadedModels.Count} 个模型。");
|
||||
|
||||
Reference in New Issue
Block a user