保存图片更新试试

This commit is contained in:
xhm\HP 2025-04-02 14:41:26 +08:00
parent 409089e2ca
commit babc40d36a
6 changed files with 472 additions and 43 deletions

View File

@ -3,10 +3,42 @@ using System.ComponentModel;
using System.Drawing.Imaging;
using AntdUI;
using DH.Commons.Enums;
using HalconDotNet;
using OpenCvSharp;
namespace DH.Commons.Base
{
public class MatSet
{
public DateTime ImageTime { get; set; } = DateTime.Now;
private string id = "";
public string Id
{
get
{
if (string.IsNullOrWhiteSpace(id))
{
id = ImageTime.ToString("HHmmssfff");
}
return id;
}
set
{
id = value;
}
}
public string CameraId { get; set; }
public Mat _mat { get; set; } = null;
public ImageFormat _imageFormat { get; set; } = ImageFormat.Jpeg;
public virtual void Dispose()
{
_mat?.Dispose();
_mat = null;
}
}
public class CameraBase : NotifyProperty
{

View File

@ -9,6 +9,7 @@ using AntdUI;
using static DH.Commons.Enums.EnumHelper;
using System.Text.Json.Serialization;
using DH.Commons.Enums;
using System.Drawing.Imaging;
namespace DH.Commons.Base
{
@ -104,20 +105,20 @@ namespace DH.Commons.Base
/// </summary>
public class DetectionResultDetail
{
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public string LabelBGR { get; set; }//识别到对象的标签BGR
public int LabelNo { get; set; } // 识别到对象的标签索引
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public string LabelName { get; set; }//识别到对象的标签名称
public double Score { get; set; }//识别目标结果的可能性、得分
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public string LabelDisplay { get; set; }//识别到对象的 显示信息
@ -138,10 +139,10 @@ namespace DH.Commons.Base
public class MLResult
{
public bool IsSuccess = false;
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public string ResultMessage;
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public Bitmap ResultMap;
public List<DetectionResultDetail> ResultDetails = new List<DetectionResultDetail>();
@ -165,8 +166,7 @@ namespace DH.Commons.Base
public bool IsGPU;
public int GPUId;
public float Score_thre;
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
public MLInit(string modelFile, bool isGPU, int gpuId, float score_thre)
@ -191,7 +191,25 @@ namespace DH.Commons.Base
}
public class DetectStationResult
{
public string Pid { get; set; }
public DateTime ImageTime { get; set; } = DateTime.Now;
private string id = "";
public string Id
{
get
{
if (string.IsNullOrWhiteSpace(id))
{
id = ImageTime.ToString("HHmmssfff");
}
return id;
}
set
{
id = value;
}
}
public string Pid { get; set; }
public string TempPid { get; set; }
@ -237,35 +255,24 @@ namespace DH.Commons.Base
/// 预处理阶段已经NG
/// </summary>
public bool IsPreTreatNG { get; set; } = false;
/// <summary>
/// 检测原图
/// </summary>
public Bitmap DetectionOriginImage { get; set; }
/// <summary>
/// 目标检测NG
/// </summary>
public bool IsObjectDetectNG { get; set; } = false;
public ImageFormat ImageFormat { get; set; } = ImageFormat.Jpeg;
public DateTime EndTime { get; set; }
public string ImageSaveDirectory { get; set; }
public int StationDetectElapsed { get; set; }
public static string NormalizeAndClean(string input)
{
public bool SaveOKOriginal = false;
public bool SaveNGOriginal = false;
public bool SaveOKDetect = false;
public bool SaveNGDetect = false;
if (input == null) return null;
// Step 1: 标准化字符编码为 Form C (规范组合)
string normalizedString = input.Normalize(NormalizationForm.FormC);
// Step 2: 移除所有空白字符,包括制表符和换行符
string withoutWhitespace = Regex.Replace(normalizedString, @"\s+", "");
// Step 3: 移除控制字符 (Unicode 控制字符,范围 \u0000 - \u001F 和 \u007F)
string withoutControlChars = Regex.Replace(withoutWhitespace, @"[\u0000-\u001F\u007F]+", "");
// Step 4: 移除特殊的不可见字符(如零宽度空格等)
string cleanedString = Regex.Replace(withoutControlChars, @"[\u200B\u200C\u200D\uFEFF]+", "");
return cleanedString;
}
}
public class RelatedCamera : NotifyProperty
@ -502,7 +509,7 @@ namespace DH.Commons.Base
private AntList<SizeTreatParam> _sizeTreatParamList = new AntList<SizeTreatParam>();
private CustomizedPoint _showLocation = new CustomizedPoint();
private string _imageSaveDirectory;
private bool _saveOKOriginal = false;
private bool _saveNGOriginal = false;
private bool _saveOKDetect = false;
@ -620,7 +627,19 @@ namespace DH.Commons.Base
OnPropertyChanged(nameof(IsAddStation));
}
}
[Category("图片保存")]
[DisplayName("图片保存文件夹")]
[Description("图片保存文件夹")]
public virtual string ImageSaveDirectory
{
get => _imageSaveDirectory;
set
{
if (_imageSaveDirectory == value) return;
_imageSaveDirectory = value;
OnPropertyChanged(nameof(ImageSaveDirectory));
}
}
[Category("1.预处理(视觉算子)")]
[DisplayName("预处理-算法文件路径")]
public string HalconAlgorithemPath_Pre

View File

@ -0,0 +1,107 @@

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DH.Commons.Helper;
namespace DH.Commons.Enums
{
public class ImageSaveHelper
{
public event Action<DateTime, string> OnImageSaveExceptionRaised;
//private string baseDirectory = "";
//public string BaseDirectory
//{
// get => baseDirectory;
// set
// {
// baseDirectory = value;
// if (string.IsNullOrWhiteSpace(baseDirectory) || !Path.IsPathRooted(baseDirectory))
// {
// baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images");
// }
// }
//}
public bool EnableImageSave { get; set; } = true;
public ImageSaveHelper() { }
public ImageSaveHelper(bool enableImageSave = true)
{
EnableImageSave = enableImageSave;
}
object lockObj = new object();
////耗时操作从 _taskFactory分配线程
//public TaskFactory _taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning);
readonly ConcurrentQueue<ImageSaveSet> _imageQueue = new ConcurrentQueue<ImageSaveSet>();
Task _saveTask = null;
readonly object _saveLock = new object();
public async void ImageSaveAsync(ImageSaveSet set)
{
if (!EnableImageSave)
return;
await Task.Run(() =>
{
_imageQueue.Enqueue(set);
lock (_saveLock)
{
if (_saveTask == null)
{
_saveTask = Task.Run(async () =>
{
try
{
while (true)
{
while (_imageQueue.Count > 0)
{
if (_imageQueue.TryDequeue(out ImageSaveSet saveSet))
{
if (!Directory.Exists(Path.GetDirectoryName(saveSet.FullName)))
{
Directory.CreateDirectory(Path.GetDirectoryName(saveSet.FullName));
}
if (saveSet.SaveImage != null)
{
saveSet.SaveImage.Save(saveSet.FullName, saveSet.ImageFormat);
saveSet.SaveImage.Dispose();
}
saveSet = null;
}
}
await Task.Delay(2000);
}
}
catch (Exception ex)
{
OnImageSaveExceptionRaised?.Invoke(DateTime.Now, $"图片保存异常:{ex.GetExceptionMessage()}");
}
});
}
}
});
}
}
public class ImageSaveSet
{
public string FullName { get; set; }//带后缀 全路径
public Bitmap SaveImage { get; set; }
public ImageFormat ImageFormat { get; set; } = ImageFormat.Jpeg;
}
}

View File

@ -1,10 +1,13 @@
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Reflection.Metadata;
using System.Xml.Linq;
using DH.Commons.Base;
using DH.Commons.Enums;
using DH.Commons.Helper;
using DVPCameraType;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using static System.Net.Mime.MediaTypeNames;
using LogLevel = DH.Commons.Enums.EnumHelper.LogLevel;
@ -28,6 +31,8 @@ namespace DH.Devices.Camera
public LoggerHelper LoggerHelper { get; set; } = new LoggerHelper();
public event Action<LogMsg> OnLog;
public ConcurrentDictionary<string, MatSet> _imageSetList = new ConcurrentDictionary<string, MatSet>();
public Do3ThinkCamera()
{
@ -360,9 +365,17 @@ namespace DH.Devices.Camera
break;
}
Mat smat = cvImage.Clone();
var imageSet = new MatSet
{
_mat = smat,
};
InitialImageSet(imageSet);
OnHImageOutput?.Invoke(DateTime.Now, this, smat);
//存图
DisplayAndSaveOriginImage(imageSet.Id);
@ -378,7 +391,86 @@ namespace DH.Devices.Camera
}
return 0;
}
public void InitialImageSet(MatSet set)
{
//if (saveOption != null)
//{
// set.ImageSaveOption = saveOption.Copy();
//}
//set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
//set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
//set.IsAddtionalSaved = string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType);
set.CameraId = this.CameraName;
set.ImageTime = DateTime.Now;
_imageSetList[set.Id] = set;
}
public virtual async void DisplayAndSaveOriginImage(string imgSetId)
{
MatSet set = _imageSetList.Values.FirstOrDefault(u => u.Id == imgSetId);
if (set != null && set._mat != null)
{
await Task.Run(() =>
{
Bitmap showImage = set._mat.ToBitmap();
// showImage.Save("D:/test333.bmp");
// Marshal.Copy(pbyteImageBuffer, 0, (IntPtr)lAddrImage, (int)dwBufferSize);
// Bitmap saveImage = showImage?.CopyBitmap();
// saveImage.Save("d://TEST444.BMP");
// OnShowImageUpdated?.Invoke(this, showImage, imgSetId);
if (IsSavePicEnabled)
{
string fullname = Path.Combine(ImageSaveDirectory, $"{CameraName}_{set.Id}.{set._imageFormat.ToString().ToLower()}");
ImageSaveAsync(fullname, showImage);
}
//释放 himage
ClearImageSet(set);
});
}
}
static object _imageSetLock = new object();
public void ClearImageSet(MatSet set)
{
try
{
bool flag = false;
lock (_imageSetLock)
{
flag = _imageSetList.TryRemove(set.Id, out set);
if (flag)
{
set.Dispose();
}
//LogAsync(DateTime.Now, $"{Name}移除图片信息{(flag ? "成功" : "失败")},当前缓存数量:{_imageSetList.Count}", "");
}
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"清理图片缓存异常,当前缓存数量{_imageSetList.Count},{ex.GetExceptionMessage()}");
}
}
public ImageSaveHelper ImageSaveHelper { get; set; } = new ImageSaveHelper();
public virtual void ImageSaveAsync(string fullName, Bitmap map)
{
if (!IsSavePicEnabled)
{
map?.Dispose();
return;
}
ImageSaveSet imageSaveSet = new ImageSaveSet()
{
FullName = fullName,
SaveImage = map,
};
ImageSaveHelper.ImageSaveAsync(imageSaveSet);
}
public override bool CameraDisConnect()
{
try

View File

@ -76,6 +76,12 @@ namespace DH.Devices.Vision
//未能获得检测配置
return detectResult;
}
detectResult.ImageSaveDirectory=detectConfig.ImageSaveDirectory;
detectResult.SaveNGDetect=detectConfig.SaveNGDetect;
detectResult.SaveNGOriginal=detectConfig.SaveNGOriginal;
detectResult.SaveOKDetect=detectConfig.SaveOKDetect;
detectResult.SaveOKOriginal=detectConfig.SaveOKOriginal;
detectResult.DetectionOriginImage = originImgSet.Clone().ToBitmap();
Stopwatch sw = new Stopwatch();
#region 1.
sw.Start();
@ -952,15 +958,15 @@ namespace DH.Devices.Vision
});
}
//if (detectResult.realSpecs != null && detectResult.realSpecs?.Count > 0)
//{
// detectResult.realSpecs.ForEach(d =>
// {
// displayTxt +=
// $"{d.Code} :{d.ActualValue} \r\n";
// });
//}
Bitmap resultMask=result.ToBitmap();
if (detectResult.realSpecs != null && detectResult.realSpecs?.Count > 0)
{
detectResult.realSpecs.ForEach(d =>
{
displayTxt +=
$"{d.Code} :{d.ActualValue} \r\n";
});
}
Bitmap resultMask =result.ToBitmap();
//if (detectResult.VisionImageSet.DetectionResultImage == null && detectResult.VisionImageSet.SizeResultImage == null)
//{
// return;
@ -995,7 +1001,7 @@ namespace DH.Devices.Vision
DetectionDone(DetectionId, resultMask, detectionResultShapes);
//SaveDetectResultImageAsync(detectResult);
SaveDetectResultImageAsync(detectResult);
// SaveDetectResultCSVAsync(detectResult);
}
catch (Exception ex)
@ -1010,6 +1016,178 @@ namespace DH.Devices.Vision
}
});
}
/// <summary>
///图片异步保存
/// </summary>
public void SaveDetectResultImageAsync(DetectStationResult detectResult)
{
string format = detectResult.ImageFormat.ToString().ToLower();
//根目录
string rootPath = Path.Combine(detectResult.ImageSaveDirectory,
DateTime.Now.ToString("yyyyMMdd"), BatchNO, detectResult.DetectName);
if (detectResult.ResultState != ResultState.OK)
{
// NG原图
if (detectResult.SaveNGOriginal && detectResult.DetectionOriginImage != null)
{
string prefix = Path.Combine(rootPath, "NGRawImages", detectResult.ResultLabel);
string fullname = Path.Combine(prefix, $"{detectResult.Pid}_NGRawImage_{detectResult.DetectName}_{detectResult.Id}.{format}");
SaveImageAsync(fullname, detectResult.DetectionOriginImage, detectResult.ImageFormat);
}
//NG结果图
if (detectResult.SaveOKDetect && detectResult.DetectionOriginImage != null)
{
// 没有预处理,则保存原始图+检测结果图
// if (detectResult.VisionImageSet.PreTreatedBitmap == null)
{
//string displayTxt = detectResult.ResultState.ToString() + "\r\n";
string displayTxt = "";
detectResult.DetectDetails.ForEach(d =>
{
displayTxt += $"{d.LabelName} score:{d.Score.ToString("f2")} area:{d.Area.ToString("f2")}\r\n";
});
if (detectResult.realSpecs != null && detectResult.realSpecs?.Count > 0)
{
detectResult.realSpecs.ForEach(d =>
{
displayTxt +=
$"{d.Code} score:{d.ActualValue} \r\n";
});
}
Bitmap resultMask = detectResult.DetectionOriginImage.CopyBitmap();
Bitmap preTreatedBitmap = detectResult.DetectionOriginImage.CopyBitmap();
List<IShapeElement> detectionResultShapes = new List<IShapeElement>(detectResult.DetectionResultShapes);
DetectResultDisplay resultDisplay = new DetectResultDisplay(detectResult, resultMask, displayTxt);
detectionResultShapes.Add(resultDisplay);
Bitmap resultMap = GetResultImage(resultMask, detectionResultShapes);
resultDisplay.Dispose();
detectionResultShapes.Clear();
Bitmap detectionFitImage = StaticHelper.HConnectBitmap(preTreatedBitmap, resultMap);
string prefix = Path.Combine(rootPath, "NGFitImages", detectResult.ResultLabel);
string fullname = Path.Combine(prefix, $"{detectResult.Pid}_NGFitImage_{detectResult.DetectName}_{detectResult.Id}.{format}");
SaveImageAsync(fullname, detectionFitImage, detectResult.ImageFormat);
resultMask?.Dispose();
preTreatedBitmap?.Dispose();
resultMap?.Dispose();
detectionFitImage?.Dispose();
}
}
}
else
{ // OK原图
if (detectResult.SaveOKOriginal && detectResult.DetectionOriginImage != null)
{
string prefix = Path.Combine(rootPath, "OKRawImages", detectResult.ResultLabel);
string fullname = Path.Combine(prefix, $"{detectResult.Pid}_OKRawImage_{detectResult.DetectName}_{detectResult.Id}.{format}");
SaveImageAsync(fullname, detectResult.DetectionOriginImage, detectResult.ImageFormat);
}
//ok结果图
if (detectResult.SaveOKDetect && detectResult.DetectionOriginImage != null)
{
// 没有预处理,则保存原始图+检测结果图
// if (detectResult.VisionImageSet.PreTreatedBitmap == null)
{
//string displayTxt = detectResult.ResultState.ToString() + "\r\n";
string displayTxt = "";
detectResult.DetectDetails.ForEach(d =>
{
displayTxt += $"{d.LabelName} score:{d.Score.ToString("f2")} area:{d.Area.ToString("f2")}\r\n";
});
if (detectResult.realSpecs != null && detectResult.realSpecs?.Count > 0)
{
detectResult.realSpecs.ForEach(d =>
{
displayTxt +=
$"{d.Code} score:{d.ActualValue} \r\n";
});
}
Bitmap resultMask = detectResult.DetectionOriginImage.CopyBitmap();
Bitmap preTreatedBitmap = detectResult.DetectionOriginImage.CopyBitmap();
List<IShapeElement> detectionResultShapes = new List<IShapeElement>(detectResult.DetectionResultShapes);
DetectResultDisplay resultDisplay = new DetectResultDisplay(detectResult, resultMask, displayTxt);
detectionResultShapes.Add(resultDisplay);
Bitmap resultMap = GetResultImage(resultMask, detectionResultShapes);
resultDisplay.Dispose();
detectionResultShapes.Clear();
Bitmap detectionFitImage = StaticHelper.HConnectBitmap(preTreatedBitmap, resultMap);
string prefix = Path.Combine(rootPath, "OKFitImages", detectResult.ResultLabel);
string fullname = Path.Combine(prefix, $"{detectResult.Pid}_" +
$"OKFitImage_{detectResult.DetectName}_{detectResult.Id}.{format}");
SaveImageAsync(fullname, detectionFitImage, detectResult.ImageFormat);
resultMask?.Dispose();
preTreatedBitmap?.Dispose();
resultMap?.Dispose();
detectionFitImage?.Dispose();
}
}
}
}
public virtual Bitmap GetResultImage(Bitmap baseImage, List<IShapeElement> eleList)
{
try
{
// 重新生成画布 避免 无法从带有索引像素格式的图像创建graphics对象
Bitmap image = new Bitmap(baseImage.Width, baseImage.Height);
using (Graphics g = Graphics.FromImage(image))
{
g.DrawImage(baseImage, 0, 0);
eleList.ForEach(e =>
{
e.State = ElementState.Normal;
e.Draw(g);
});
}
return image;
}
catch (Exception ex)
{
LogAsync(DateTime.Now, LogLevel.Exception, $"获取叠加结果图片异常:{ex.GetExceptionMessage()}");
return null;
}
}
}
}

View File

@ -270,6 +270,7 @@ namespace DHSoftware
cam.CameraName = cameraBase.CameraName;
cam.CameraIP = cameraBase.CameraIP;
cam.IsEnabled = cameraBase.IsEnabled;
cam.ImageSaveDirectory = "D://Cam1//";
Cameras.Add(cam);
cam.OnLog -= _visionEngine_OnLog;
cam.OnLog += _visionEngine_OnLog;