using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using LeatherApp.Utils;
using Models;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using SqlSugar;
namespace LeatherApp.Device
{
public class DefectLib : IDisposable
{
//private const string dll_path = "yoloOrt.dll";
//[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
//extern static IntPtr CreateDetector(string model_path, string labels_path, bool is_gpu, int numberThreads);
private const string dll_path = "yolo_trt.dll";
[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr CreateDetector(string model_path);
[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
extern static bool DestroyDetector(IntPtr detector);
///
/// 非0值表示成功,0表示失败 返回小图张数
///
///
///
[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern int GetBatchSize(IntPtr detector);
//[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
//extern static bool Detect1(IntPtr detector, ref byte bgrs_data,
// int image_num, int width, int height, int channels,
// float conf_threshold, float iou_threshold,
// ref int output, int output_size, ref int object_num);
[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
static extern bool Detect(IntPtr detector, ref byte bgrs_data,
int image_num, int width, int height, int channels,
float conf_threshold, float iou_threshold,
ref float output, int output_size, ref int object_num,
int expand_pixel);
//-----
//private const string dll_path = "yolo_trt .dll";
//[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
//static extern IntPtr CreateDetector(string model_path);
//[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
//static extern bool DestroyDetector(IntPtr detector);
//[DllImport(dll_path, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
//static extern bool Detect(IntPtr detector, ref byte bgrs_data,
// int image_num, int width, int height, int channels,
// float conf_threshold, float iou_threshold,
// ref int output, int output_size, ref int object_num);
//---
private IntPtr detector = IntPtr.Zero;
private readonly int image_width = 2048;
private readonly int image_hight = 2048;
private readonly int image_channels = 3;
private readonly int image_bytes = 2048 * 2048 * 3;
private readonly int detect_elem_size = 7; //维度数据结果,x y w h conf classid
private readonly int detect_max_object_num = 20;//这个指的是一张子图可能最多给你返回的目标个球
//
public Action QueueCountEvent;//0/1/2, 数量
public Action WarningEvent;
///
/// 检测结果JSON(原图,结果)
///
public Action finishEvent;
///
/// 是否打开设备成功
///
public bool IsInit { get; private set; } = false;
string rootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private Thread t_task, t_task_operation ,t_task_maketag;
//=======task list
private List taskList = new List();
private List taskOperationList = new List();
private List taskMakeTagList = new List();
private string preModelName = "";
public DefectLib()
{
}
public bool start()
{
//List DefectLabelInfoList = new List();
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 1953, y = 429, w = 200, h = 200, classId = 1, confidence = 0.8, contrast = 0.9, i = 0, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() {x = 1953,y = 929,w=900,h = 400,classId = 11,confidence = 0.8, contrast = 0.9, i = 0,j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 3169, y = 1029, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 1, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 4721, y = 919, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 2, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 6145, y = 829, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 3, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 8073, y = 929, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 4, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 9407, y = 929, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 5, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 10801, y = 999, w = 900, h = 400, classId = 11, confidence = 0.8, contrast = 0.9, i = 6, j = 0 });
//DefectLabelInfoList.Add(new DefectLabelInfo() { x = 12465, y = 629, w = 900, h = 800, classId = 11, confidence = 0.8, contrast = 0.9, i = 7, j = 0 });
//DefectLabelInfoList = HeBingDefect(16384, DefectLabelInfoList);
try
{
//detector = CreateDetector(Config.model_path, Config.labels_path, true, 6);
//if (detector == IntPtr.Zero)
// throw new Exception("模型初始化失败!");
preModelName = "";
IsInit = true;
taskList.Clear();
taskOperationList.Clear();
taskMakeTagList.Clear();
t_task = new System.Threading.Thread(runStep);
t_task.IsBackground = true;
t_task.Start();
t_task_operation = new System.Threading.Thread(run2);
t_task_operation.IsBackground = true;
t_task_operation.Start();
t_task_maketag = new System.Threading.Thread(run3);
t_task_maketag.IsBackground = true;
t_task_maketag.Start();
return true;
}
catch (Exception ex)
{
WarningEvent?.Invoke(DateTime.Now,WarningEnum.High, ex.Message);
return false;
}
}
public void stop()
{
if (!IsInit) return;
try
{
IsInit = false;
//timer.Elapsed -= Timer_Elapsed;
preModelName = "";
//释放模型
if (detector != IntPtr.Zero)
{
DestroyDetector(detector);
detector = IntPtr.Zero;
}
if (t_task != null)
{
bool b = t_task.Join(5000);
if (!b) t_task.Abort();
t_task = null;
}
if (t_task_operation != null)
{
bool b = t_task_operation.Join(5000);
if (!b) t_task_operation.Abort();
t_task_operation = null;
}
if (t_task_maketag != null)
{
bool b = t_task_maketag.Join(5000);
if (!b) t_task_maketag.Abort();
t_task_maketag = null;
}
taskList.Clear();
taskOperationList.Clear();
taskMakeTagList.Clear();
}
catch { }
}
///
///
///
/// 多个相机图像总宽(外部去除重合部分)
///
public int GetWidthForResize(int sumWidth)
{
int count =(int) Math.Round(sumWidth * 1.0f / image_width, 0);
count = 8;
return count * image_width;
//int count = sumWidth / image_width;
////int remainder = sumWidth % image_width;
//if (count % 2 == 0)
// return count * image_width;
//else
// return count * image_width+ image_width;
}
///
/// 保存图片
///
/// 图片
/// 保存路径
/// 保存名称
private void SaveImage(Bitmap Img, string pictureUrl, string pictureName)
{
if (!Directory.Exists(pictureUrl))
{
Directory.CreateDirectory(pictureUrl);
}
FileInfo FileUrl = new FileInfo(pictureUrl);//防止路径中有日期导致路径错误
try
{
using (Bitmap bitmap = new Bitmap(Img))
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Bmp);
System.Drawing.Image img = System.Drawing.Image.FromStream(stream);
//img.Save(FileUrl +"\\"+ pictureName);
//img.Dispose();
string szURL = FileUrl + "\\" + pictureName;
img.Save(szURL, ImageFormat.Bmp);
img.Dispose();
}
}
}
catch (Exception)
{
if (Img != null)
{
Img.Dispose();
}
}
}
private void runStep()
{
int step = 0;
while (IsInit)
{
if (taskList.Count < 1)
{
Thread.Sleep(20);
continue;
}
//
step = 1;
var task = pop();
try
{
if (task != null)
{
Stopwatch stopwatch = Stopwatch.StartNew();
step =2;
if (preModelName != task.modelName)
{
step = 3;
//先释放模型
if (detector != IntPtr.Zero)
{
step = 4;
DestroyDetector(detector);
detector = IntPtr.Zero;
}
step = 5;
detector = CreateDetector($"./models/{task.modelName}");
if (detector == IntPtr.Zero)
throw new Exception($"模型({task.modelName})初始化失败!");
step = 6;
preModelName = task.modelName;
}
//源图
//Bitmap bmp = yolo1.Read2Bmp(file_path);
//切割图像,输入图像格式14208*10640
stopwatch.Start();
//task.resizeBmp = OpenCVUtil.resize( task.bmp.Clone(), task.resize.Width, task.resize.Height);//在外面已做了resize
//Cv2.CvtColor(task.bmp, task.bmpBgr2rgb, ColorConversionCodes.BGR2RGB);
//task.bmps_cut = OpenCVToCutsMat(task.bmpBgr2rgb, image_width, image_hight); //这里cut时之前加的clone
step = 7;
task.bmps_cut = OpenCVToCutsMat(task.bmp, image_width, image_hight); //这里cut时之前加的clone
stopwatch.Stop();
task.stopwatch[0] = stopwatch.ElapsedMilliseconds;
//Resize图像
//stopwatch.Restart();
//task.bmps_resize = yolo1.OpenCVToResizesMat(task.bmps_cut, task.resize.Width, task.resize.Height);
//stopwatch.Stop();
//task.stopwatch[1] = stopwatch.ElapsedMilliseconds;
//预处理模型
//stopwatch.Restart();
//task.tensors = yolo1.PreprocessImageMat(task.bmps_resize);
//stopwatch.Stop();
//task.stopwatch[2] = stopwatch.ElapsedMilliseconds;
step = 8;
lock (taskOperationList)
{
taskOperationList.Add(task);
QueueCountEvent?.BeginInvoke(1, taskOperationList.Count, null, null);
}
step = 9;
}
}
catch (Exception ex)
{
WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"DefectLib task1 err({step}):" + ex.Message);
task.isSucceed = false;
task.resultInfo = ex.Message;
callback(task);
}
Thread.Sleep(5);
}
}
//推理
private void run2()
{
QualifiedLimit qualifiedLimit;
while (IsInit)
{
if (taskOperationList.Count < 1)
{
Thread.Sleep(20);
continue;
}
//
var task = pop2();
int cut_count=0,step = 0;
try
{
if (task != null && task.bmps_cut.Count()>0)
{
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, "DefectLib tasks 运行推理...");
cut_count = task.bmps_cut.Count();
Stopwatch stopwatch = Stopwatch.StartNew();
//====推理(必需单队列)
stopwatch.Start();
// 把数据转为byte数组,【h, w, c】的bgr格式,第一张在前,第二张在后
byte[] imgData = new byte[image_bytes * cut_count];
for (int i = 0; i < cut_count; i++)
Marshal.Copy(task.bmps_cut[i].Data, imgData, image_bytes * i, image_bytes);
step = 1;
stopwatch.Stop();
task.stopwatch[1] = stopwatch.ElapsedMilliseconds;
stopwatch.Restart();
task.output = new float[cut_count * detect_elem_size * detect_max_object_num];
task.output_num = new int[cut_count];
//执行推理
step =2;
bool ok = Detect(detector, ref imgData[0], cut_count, image_width, image_hight, image_channels,
0.25f, 0.45f, ref task.output[0], task.output.Length, ref task.output_num[0],Config.expand_pixel);
//bool ok = Detect(_detector, ref imgData[0], imgs.Count, ImageWidth, image_hight, image_channels,
// 0.25f, 0.45f, ref output[0], output.Length, ref output_num[0]);
step = 3;
stopwatch.Stop();
task.stopwatch[2] = stopwatch.ElapsedMilliseconds;
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"DefectLib tasks 结果推理={ok}");
for (int i = 0; i < cut_count; i++)
task.bmps_cut[i].Dispose();
if (ok == false)
throw new Exception($"推理失败或者输入数组太小({cut_count})");
//
lock (taskMakeTagList)
{
taskMakeTagList.Add(task);
QueueCountEvent?.BeginInvoke(2, taskMakeTagList.Count, null, null);
}
}
}
catch (Exception ex)
{
WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"DefectLib task2 err({step}):({cut_count}){ ex.Message}");
task.isSucceed = false;
task.resultInfo = ex.Message;
callback(task);
}
Thread.Sleep(5);
}
}
private class DefectLabelInfo
{
public int x { get; set; }
public int y { get; set; }
public int w { get; set; }
public int h { get; set; }
public int classId { get; set; }
public double confidence { get; set; } //置信度
public double contrast { get; set; }//对比度
public double cmW { get; set; }
public double cmH { get; set; }
public int i { get; set; }//小图index
public int j { get; set; }//缺陷index
}
private double ContrastLow = 0.8;
private double ContrastTop = 1.2;
//private double ContrastToPercent(double val)
//{
// if (val < ContrastLow)
// return 0;
// else if (val > ContrastTop)
// return 100;
// double temp = 100 / (ContrastTop - ContrastLow);
// return temp * (val - ContrastLow);
//}
//private double PercentToContrast(double val)
//{
// double temp = val * (ContrastTop - ContrastLow);
// return temp + ContrastLow;
//}
private double ContrastToPercent(double val)
{
if (val < ContrastLow)
return 0;
else if (val > ContrastTop)
return 100;
double temp = 100 / (ContrastTop - ContrastLow);
return Math.Round(temp * (val - ContrastLow), 2);
}
private double PercentToContrast(double val)
{
double tt = 1;
if (val > 100)
tt = 100;
else if (val < 0)
tt = 0;
else
tt = val;
double temp = tt / 100 * (ContrastTop - ContrastLow);
return temp + ContrastLow;
}
private List HeBingDefect(int Width, List DefectLabelInfoList)
{
List outList = new List();
List HeBingList = new List();
List XcHeBingList = new List();
List xPos = new List();
List ZXD = new List();
List HeBingList2 = new List();
List XcHeBingList2 = new List();
List xPos2 = new List();
List ZXD2 = new List();
List HeBingList3 = new List();
List XcHeBingList3 = new List();
List xPos3 = new List();
List ZXD3 = new List();
List HeBingList4 = new List();
List XcHeBingList4 = new List();
List xPos4 = new List();
List ZXD4 = new List();
DefectLabelInfo stpoint = DefectLabelInfoList[0];
int colNum = Width / image_width;
//寻找在一条线上
for (int q = 0; q < DefectLabelInfoList.Count; q++)
{
if (Config.getDefectCode(DefectLabelInfoList[q].classId) == "jietou")
{
int max = stpoint.y + 500;
int min = stpoint.y - 500 > 0? stpoint.y - 500:0;
if (DefectLabelInfoList[q].y >= min && DefectLabelInfoList[q].y <= max)
{
HeBingList.Add(DefectLabelInfoList[q]);
xPos.Add((DefectLabelInfoList[q].i % colNum) * image_width + DefectLabelInfoList[q].x);
ZXD.Add(DefectLabelInfoList[q].confidence);
}
else
XcHeBingList.Add(DefectLabelInfoList[q]);
}
else if (Config.getDefectCode(DefectLabelInfoList[q].classId) == "hengdang")
{
int max = stpoint.y + 500;
int min = stpoint.y - 500 > 0 ? stpoint.y - 500 : 0;
if (DefectLabelInfoList[q].y >= min && DefectLabelInfoList[q].y <= max)
{
HeBingList2.Add(DefectLabelInfoList[q]);
xPos2.Add((DefectLabelInfoList[q].i % colNum) * image_width + DefectLabelInfoList[q].x);
ZXD2.Add(DefectLabelInfoList[q].confidence);
}
else
XcHeBingList2.Add(DefectLabelInfoList[q]);
}
else if (Config.getDefectCode(DefectLabelInfoList[q].classId) == "jt")
{
int max = stpoint.y + 500;
int min = stpoint.y - 500 > 0 ? stpoint.y - 500 : 0;
if (DefectLabelInfoList[q].y >= min && DefectLabelInfoList[q].y <= max)
{
HeBingList3.Add(DefectLabelInfoList[q]);
xPos3.Add((DefectLabelInfoList[q].i % colNum) * image_width + DefectLabelInfoList[q].x);
ZXD3.Add(DefectLabelInfoList[q].confidence);
}
else
XcHeBingList3.Add(DefectLabelInfoList[q]);
}
else if (Config.getDefectCode(DefectLabelInfoList[q].classId) == "tcy")
{
int max = stpoint.y + 500;
int min = stpoint.y - 500 > 0 ? stpoint.y - 500 : 0;
if (DefectLabelInfoList[q].y >= min && DefectLabelInfoList[q].y <= max)
{
HeBingList4.Add(DefectLabelInfoList[q]);
xPos4.Add((DefectLabelInfoList[q].i % colNum) * image_width + DefectLabelInfoList[q].x);
ZXD4.Add(DefectLabelInfoList[q].confidence);
}
else
XcHeBingList4.Add(DefectLabelInfoList[q]);
}
else
outList.Add(DefectLabelInfoList[q]);
}
//递归下次合并数据
List dg1 = new List();
List dg2 = new List();
List dg3 = new List();
List dg4 = new List();
if (XcHeBingList.Count >0)
dg1 = HeBingDefect(Width, XcHeBingList);
if (XcHeBingList2.Count > 0)
dg2 = HeBingDefect(Width, XcHeBingList2);
if (XcHeBingList3.Count > 0)
dg3 = HeBingDefect(Width, XcHeBingList3);
if (XcHeBingList4.Count > 0)
dg4 = HeBingDefect(Width, XcHeBingList4);
//多个jietou合并
if (HeBingList.Count>0)
{
var stIt = HeBingList.Find(x => (x.i % colNum) * image_width + x.x == xPos.Min());
var edIt = HeBingList.Find(x => (x.i % colNum) * image_width + x.x == xPos.Max());
var eZXD = HeBingList.Find(x => x.confidence == ZXD.Max());
int newW = Math.Abs(((edIt.i % colNum) * image_width + edIt.x) - ((stIt.i % colNum) * image_width + stIt.x)) + edIt.w;
outList.Add(new DefectLabelInfo() {
x=stIt.x,
y=edIt.y,
w = newW, //多图叠加
h = edIt.h,
classId = eZXD.classId,
confidence = eZXD.confidence,
contrast = eZXD.contrast,
cmH = Math.Round(edIt.h * 1.0 / Config.cm2px_y, 2),
cmW = Math.Round(newW * 1.0 / Config.cm2px_x, 2),
i = stIt.i,
j = stIt.j,
});
}
//多个hengdang合并
if (HeBingList2.Count > 0)
{
var stIt = HeBingList2.Find(x => (x.i % colNum) * image_width + x.x == xPos2.Min());
var edIt = HeBingList2.Find(x => (x.i % colNum) * image_width + x.x == xPos2.Max());
var eZXD = HeBingList2.Find(x => x.confidence == ZXD2.Max());
int newW = Math.Abs(((edIt.i % colNum) * image_width + edIt.x) - ((stIt.i % colNum) * image_width + stIt.x)) + edIt.w;
outList.Add(new DefectLabelInfo()
{
x = stIt.x,
y = edIt.y,
w = newW, //多图叠加
h = edIt.h,
classId = eZXD.classId,
confidence = eZXD.confidence,
contrast = eZXD.contrast,
cmH = Math.Round(edIt.h * 1.0 / Config.cm2px_y, 2),
cmW = Math.Round(newW * 1.0 / Config.cm2px_x, 2),
i = stIt.i,
j = stIt.j,
});
}
//多个jt合并
if (HeBingList3.Count > 0)
{
var stIt = HeBingList3.Find(x => (x.i % colNum) * image_width + x.x == xPos3.Min());
var edIt = HeBingList3.Find(x => (x.i % colNum) * image_width + x.x == xPos3.Max());
var eZXD = HeBingList3.Find(x => x.confidence == ZXD3.Max());
int newW = Math.Abs(((edIt.i % colNum) * image_width + edIt.x) - ((stIt.i % colNum) * image_width + stIt.x)) + edIt.w;
outList.Add(new DefectLabelInfo()
{
x = stIt.x,
y = edIt.y,
w = newW, //多图叠加
h = edIt.h,
classId = eZXD.classId,
confidence = eZXD.confidence,
contrast = eZXD.contrast,
cmH = Math.Round(edIt.h * 1.0 / Config.cm2px_y, 2),
cmW = Math.Round(newW * 1.0 / Config.cm2px_x, 2),
i = stIt.i,
j = stIt.j,
});
}
//多个tcy合并
if (HeBingList4.Count > 0)
{
var stIt = HeBingList4.Find(x => (x.i % colNum) * image_width + x.x == xPos4.Min());
var edIt = HeBingList4.Find(x => (x.i % colNum) * image_width + x.x == xPos4.Max());
var eZXD = HeBingList4.Find(x => x.confidence == ZXD4.Max());
int newW = Math.Abs(((edIt.i % colNum) * image_width + edIt.x) - ((stIt.i % colNum) * image_width + stIt.x)) + edIt.w;
outList.Add(new DefectLabelInfo()
{
x = stIt.x,
y = edIt.y,
w = newW, //多图叠加
h = edIt.h,
classId = eZXD.classId,
confidence = eZXD.confidence,
contrast = eZXD.contrast,
cmH = Math.Round(edIt.h * 1.0 / Config.cm2px_y, 2),
cmW = Math.Round(newW * 1.0 / Config.cm2px_x, 2),
i = stIt.i,
j = stIt.j,
});
}
outList = outList.Concat(dg1).ToList();//保留重复项
outList = outList.Concat(dg2).ToList();//保留重复项
outList = outList.Concat(dg3).ToList();//保留重复项
outList = outList.Concat(dg4).ToList();//保留重复项
return outList;
}
//打标
private void run3()
{
QualifiedLimit qualifiedLimit;
while (IsInit)
{
if (taskMakeTagList.Count < 1)
{
Thread.Sleep(20);
continue;
}
//
var task = pop3();
int liStep = 0;
try
{
Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Restart();
int cut_count = task.bmps_cut.Count();//上面bmps_cut已销毁
int colNum = task.bmp.Width / image_width;
int rowNum = task.bmp.Height / image_hight;
int count = 0;
liStep = 3;
//车用革去除接头处横档误判
bool haveJieTou = false;
List DefectLabelInfoList = new List();
for (int i = 0; i < cut_count; i++)
{
liStep = i * 100;
//task.resultInfo += $"第 {i}/{cut_count} 张小图(大图索引{task.photoIndex}): 缺陷数 = {task.output_num[i]}\n";
//task.resultInfo +=$"大图({task.tag})[{task.bmp.Width}*{task.bmp.Height}],第 {i + 1}/{cut_count} 张小图[{task.bmps_cut[i].Width}*{task.bmps_cut[i].Height}]: 瑕疵output_num = {output_num[i]}\n";
#region 检测信息汇总
for (int j = 0; j < task.output_num[i]; j++)//缺陷数
{
liStep += j;//0
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"-------");
int index = count * detect_elem_size;
// 打印输出信息(示例代码,根据实际情况进行修改和格式化)
// 获取输出信息
int x = (int)task.output[index];
int y = (int)task.output[index + 1];
int w = (int)task.output[index + 2];
int h = (int)task.output[index + 3];
int classId = (int)task.output[index + 4];
double confidence = Math.Round(task.output[index + 5], 2); //置信度
double contrast = Math.Round(task.output[index + 6], 3);//对比度
//y = image_hight-y-h;//转到右下角为原点
count++;
//var cmW = Math.Round(w * task.widthRatio / Config.cm2px_x, 2);
//var cmH = Math.Round(h * task.widthRatio / Config.cm2px_y, 2);
var cmW = Math.Round(w * 1.0 / Config.cm2px_x, 2);
var cmH = Math.Round(h * 1.0 / Config.cm2px_y, 2);
// 打印输出信息
//task.resultInfo += $"----{i}----col:{i % colNum}/{colNum} row:{i / colNum}/{colNum}-----------\n目标:{j + 1} 类别ID:{classId} 置信度:{confidence} 对比度:{contrast} 坐标:({x},{y})-({x + w},{y + h}) 宽高:w={w},h={h}; \n";
DefectLabelInfoList.Add(new DefectLabelInfo()
{
x = (int)task.output[index],
y = (int)task.output[index + 1],
w = (int)task.output[index + 2],
h = (int)task.output[index + 3],
classId = (int)task.output[index + 4],
confidence = Math.Round(task.output[index + 5], 2), //置信度
contrast = ContrastToPercent( Math.Round(task.output[index + 6], 3)),//对比度
//cmW = Math.Round(w * task.widthRatio / Config.cm2px_x, 2),
//cmH = Math.Round(h * task.widthRatio / Config.cm2px_y, 2),
cmW = Math.Round(w * 1.0 / Config.cm2px_x, 2),
cmH = Math.Round(h * 1.0 / Config.cm2px_y, 2),
i = i,
j = j,
});
}
#endregion
#if false
for (int j = 0; j < task.output_num[i]; j++)//缺陷数
{
liStep += j;//0
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"-------");
int index = count * detect_elem_size;
// 打印输出信息(示例代码,根据实际情况进行修改和格式化)
// 获取输出信息
int x = (int)task.output[index];
int y = (int)task.output[index + 1];
int w = (int)task.output[index + 2];
int h = (int)task.output[index + 3];
int classId = (int)task.output[index + 4];
double confidence = Math.Round(task.output[index + 5],2); //置信度
double contrast = Math.Round(task.output[index + 6], 3);//对比度
//y = image_hight-y-h;//转到右下角为原点
count++;
var cmW = Math.Round(w * task.widthRatio / Config.cm2px_x, 2);
var cmH = Math.Round(h * task.widthRatio / Config.cm2px_y, 2);
// 打印输出信息
task.resultInfo += $"----{i}----col:{i % colNum}/{colNum} row:{i / colNum}/{colNum}-----------\n目标:{j + 1} 类别ID:{classId} 置信度:{confidence} 对比度:{contrast} 坐标:({x},{y})-({x + w},{y + h}) 宽高:w={w},h={h}; \n";
//是否满足此产品标准
if (task.qualifiedLimitList != null && task.qualifiedLimitList.Count > 0)
{
qualifiedLimit = task.qualifiedLimitList.FirstOrDefault(m => m.Code == Config.getDefectCode(classId));
if (qualifiedLimit != null)
{
//if ((qualifiedLimit.ZXD > 0 && qualifiedLimit.ZXD > confidence)
// || (qualifiedLimit.ContrastTop + qualifiedLimit.ContrastLower > 0 && x> qualifiedLimit.ContrastLower && x < qualifiedLimit.ContrastTop)
// || (qualifiedLimit.Area > 0 && qualifiedLimit.Area > cmW * cmH))
if (confidence <= qualifiedLimit.ZXD)//confidence > qualifiedLimit.ZXD 是瑕疵 才继续判断下面的两个条件
{
task.resultInfo += $" 置信度不满足此产品瑕疵标准,跳过! \n";
continue;
}
//下限<对比度<上限:不是瑕疵
var ContrastTotal = qualifiedLimit.ContrastTop + qualifiedLimit.ContrastLower;
if (qualifiedLimit.IsOR)
{
if (!(
(qualifiedLimit.Area <= 0 || cmW * cmH >= qualifiedLimit.Area) ||
(ContrastTotal <= 0 || (contrast < qualifiedLimit.ContrastLower || contrast > qualifiedLimit.ContrastTop))))
{
task.resultInfo += $" 不满足此产品瑕疵标准,跳过! \n";
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断不是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
continue;
}
}
else
{
if (!(
(qualifiedLimit.Area<=0 || cmW * cmH >= qualifiedLimit.Area) &&
(ContrastTotal <=0 || (contrast < qualifiedLimit.ContrastLower || contrast > qualifiedLimit.ContrastTop))))
{
task.resultInfo += $" 不满足此产品瑕疵标准,跳过! \n";
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断不是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
continue;
}
}
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
}
}
liStep++;//1
//打标
var point1 = new OpenCvSharp.Point((i % colNum) * image_width + x, (i / colNum) * image_hight + y);
var point2 = new OpenCvSharp.Point(point1.X + w, point1.Y + h);
liStep++;//2
task.resultInfo += $" 转换到大图坐标(px):p1={point1.X},{point1.Y}; p2={point2.X},{point2.Y}\n";
Cv2.Rectangle(task.bmpTag, point1, point2, new Scalar(0.0, 0.0, 255.0), 1);//画打标点
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"保存第 {count} 行缺陷信息;");
var cmX = Math.Round(point1.X * task.widthRatio / Config.cm2px_x, 2);
var cmY = Math.Round((task.bmp.Height-point1.Y-h) * task.widthRatio / Config.cm2px_y, 2);//外面计Y从右下角为原点
liStep++;//3
task.resultInfo += $" 转换到大图坐标(cm)[widthRatio={task.widthRatio}]:x={cmX},y={cmY}; w={cmW},h={cmH}\n";
task.excelTable.Rows.Add($"{task.photoIndex}", cmX, cmY, cmW, cmH, j + 1, classId, confidence, contrast);
liStep++;//4
//切缺陷小图
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"保存第 {count} 个缺陷小图;");
int left, top, decX, decY;
decX = (w > 236 ? 20 : 256 - w) / 2;
decY = (h > 236 ? 20 : 256 - h) / 2;
left = point1.X - decX;
top = point1.Y - decY;
if (left < 0) left = 0;
if (top < 0) top = 0;
int width = w + decX * 2;
int height = h + decY * 2;
if (left + width > task.bmp.Width - 1) width = task.bmp.Width - left - 1;
if (top + height > task.bmp.Height - 1) height = task.bmp.Height - top - 1;
liStep++;//5
Rect roi = new Rect(left, top, width, height);
liStep++;//6
if (height < 1 || width < 1)
{
task.resultInfo += $" 打标到大图坐标Rect(px):left={left},top={top}; width={width},height={height}\n";
task.resultInfo += $" test point1.Y={point1.Y},h={h}; top={top},mat.Height={ task.bmp.Height}\n================\n";
WarningEvent?.Invoke(DateTime.Now,WarningEnum.Normal, task.resultInfo);
}
//保存
//string filename = $"{Config.appBasePath}\\temp\\{task.tag}\\{task.tag}_X{mmX.ToString()}_Y{mmY.ToString()}_W{mmW.ToString()}_H{mmH.ToString()}_目标{j + 1}_类别{classId}_置信度{confidence}.bmp";
//OpenCvSharp.Extensions.BitmapConverter.ToBitmap(new Mat(task.bmp, roi)).Save(filename, ImageFormat.Jpeg);
task.lstDefectBmp.Add(new Mat(task.bmpTag, roi).Clone());
liStep++;//7
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"缺陷小图数量:{task.lstDefectBmp.Count}");
}
#endif
}
#region 合并接头横档
liStep++;
if (DefectLabelInfoList.Count >0)
DefectLabelInfoList = HeBingDefect(task.bmp.Width, DefectLabelInfoList);
liStep++;
#endregion
//结果过滤
#region 结果过滤
//降序排序,先得到是否有接头检出
List DefectLabelInfoListByClassID = DefectLabelInfoList.OrderByDescending(t => t.classId).ToList();//降序
for (int q = 0; q < DefectLabelInfoListByClassID.Count; q++)
{
//是否满足此产品标准
if (task.qualifiedLimitList != null && task.qualifiedLimitList.Count > 0)
{
qualifiedLimit = task.qualifiedLimitList.FirstOrDefault(m => m.Code == Config.getDefectCode(DefectLabelInfoListByClassID[q].classId));
if (qualifiedLimit != null)
{
// 打印输出信息
task.resultInfo += $"----{DefectLabelInfoListByClassID[q].i}----col:{DefectLabelInfoListByClassID[q].i % colNum}/{colNum} row:{DefectLabelInfoListByClassID[q].i / colNum}/{colNum}-----------\n目标:{DefectLabelInfoListByClassID[q].j + 1} 类别ID:{DefectLabelInfoListByClassID[q].classId} 置信度:{DefectLabelInfoListByClassID[q].confidence} 对比度:{DefectLabelInfoListByClassID[q].contrast}\n";
//if ((qualifiedLimit.ZXD > 0 && qualifiedLimit.ZXD > confidence)
// || (qualifiedLimit.ContrastTop + qualifiedLimit.ContrastLower > 0 && x> qualifiedLimit.ContrastLower && x < qualifiedLimit.ContrastTop)
// || (qualifiedLimit.Area > 0 && qualifiedLimit.Area > cmW * cmH))
if (DefectLabelInfoListByClassID[q].confidence <= qualifiedLimit.ZXD)//confidence > qualifiedLimit.ZXD 是瑕疵 才继续判断下面的两个条件
{
task.resultInfo += $" 置信度不满足此产品瑕疵标准,跳过! \n";
continue;
}
//下限<对比度<上限:不是瑕疵
var ContrastTotal = qualifiedLimit.ContrastTop + qualifiedLimit.ContrastLower;
if (qualifiedLimit.IsOR)
{
//if (!(
// (ContrastTotal <= 0 || (PercentToContrast(DefectLabelInfoListByClassID[q].contrast) < qualifiedLimit.ContrastLower || PercentToContrast(DefectLabelInfoListByClassID[q].contrast) > qualifiedLimit.ContrastTop))))
//{
// task.resultInfo += $" 不满足此产品瑕疵标准,跳过! \n";
// //WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断不是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
// continue;
//}
}
else
{
if (!(ContrastTotal <= 0 || (PercentToContrast(DefectLabelInfoListByClassID[q].contrast) < qualifiedLimit.ContrastLower || PercentToContrast(DefectLabelInfoListByClassID[q].contrast) > qualifiedLimit.ContrastTop)))
{
task.resultInfo += $" 不满足此产品瑕疵标准,跳过! \n";
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断不是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
continue;
}
}
if(qualifiedLimit.Area > 0 && DefectLabelInfoListByClassID[q].cmW * DefectLabelInfoListByClassID[q].cmH < qualifiedLimit.Area)
{
task.resultInfo += $" 不满足此产品瑕疵标准面积,跳过! \n";
continue;
}
if (Config.getDefectCode(DefectLabelInfoListByClassID[q].classId) == "jietou")
haveJieTou = true;
if(haveJieTou && (Config.getDefectCode(DefectLabelInfoListByClassID[q].classId) == "hengdang"))
{
task.resultInfo += $" 判断为接头处横档,跳过! \n";
continue;
}
if (Config.getDefectCode(DefectLabelInfoListByClassID[q].classId) == "na")
{
task.resultInfo += $" 判断为正向标签na,跳过! \n";
continue;
}
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"判断是瑕疵:类别ID:{classId}; 置信度({confidence},[{qualifiedLimit.ZXD}]); isOr({qualifiedLimit.IsOR}); 面积({cmW * cmH},[{ qualifiedLimit.Area}]); 对比度({contrast},[{qualifiedLimit.ContrastLower}-{qualifiedLimit.ContrastTop}])");
}
}
liStep++;//1
//打标
//计算打标框大小,进行调整
//int dw = 0;
//int dh = 0;
//if (DefectLabelInfoListByClassID[q].w / DefectLabelInfoListByClassID[q].h > 4)
//{
// dh = DefectLabelInfoListByClassID[q].w / 4;
//}
//if (DefectLabelInfoListByClassID[q].h / DefectLabelInfoListByClassID[q].w > 4)
//{
// dw = DefectLabelInfoListByClassID[q].h / 4;
//}
var point1 = new OpenCvSharp.Point((DefectLabelInfoListByClassID[q].i % colNum) * image_width + DefectLabelInfoListByClassID[q].x, (DefectLabelInfoListByClassID[q].i / colNum) * image_hight + DefectLabelInfoListByClassID[q].y);
var point2 = new OpenCvSharp.Point(point1.X + DefectLabelInfoListByClassID[q].w, point1.Y + DefectLabelInfoListByClassID[q].h);
liStep++;//2
//task.resultInfo += $" 转换到大图坐标(px):p1={point1.X},{point1.Y}; p2={point2.X},{point2.Y}\n";
Cv2.Rectangle(task.bmpTag, point1, point2, new Scalar(0.0, 0.0, 255.0), 5);//画打标点
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"保存第 {count} 行缺陷信息;");
int px = (point1.X - task.xw) > 0 ? (point1.X - task.xw) : 0;
//var cmX = Math.Round(px * task.widthRatio / Config.cm2px_x, 2);
var cmX = Math.Round((double)px / Config.cm2px_x, 2);
//var cmY = Math.Round((task.bmp.Height - point1.Y - DefectLabelInfoListByClassID[q].h) * task.widthRatio / Config.cm2px_y, 2);//外面计Y从右下角为原点
var cmY = Math.Round((task.bmp.Height - point1.Y - DefectLabelInfoListByClassID[q].h) * 1.0 / Config.cm2px_y, 2);//外面计Y从右下角为原点
liStep++;//3
//task.resultInfo += $" 转换到大图坐标(cm)[widthRatio={task.widthRatio}]:x={cmX},y={cmY}; w={DefectLabelInfoList[q].cmW},h={DefectLabelInfoList[q].cmH}\n";
task.excelTable.Rows.Add($"{task.photoIndex}", cmX, cmY, DefectLabelInfoListByClassID[q].cmW, DefectLabelInfoListByClassID[q].cmH, DefectLabelInfoListByClassID[q].j + 1, DefectLabelInfoListByClassID[q].classId, DefectLabelInfoListByClassID[q].confidence, DefectLabelInfoListByClassID[q].contrast);
liStep++;//4
//切缺陷小图
//WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"保存第 {count} 个缺陷小图;");
int left, top, decX, decY;
decX = (DefectLabelInfoListByClassID[q].w > 236 ? 20 : 256 - DefectLabelInfoListByClassID[q].w) / 2;
decY = (DefectLabelInfoListByClassID[q].h > 236 ? 20 : 256 - DefectLabelInfoListByClassID[q].h) / 2;
left = point1.X - decX;
top = point1.Y - decY;
if (left < 0) left = 0;
if (top < 0) top = 0;
int width = DefectLabelInfoListByClassID[q].w + decX * 2;
int height = DefectLabelInfoListByClassID[q].h + decY * 2;
if (left + width > task.bmp.Width - 1) width = task.bmp.Width - left - 1;
if (top + height > task.bmp.Height - 1) height = task.bmp.Height - top - 1;
liStep++;//5
Rect roi = new Rect(left, top, width, height);
liStep++;//6
if (height < 1 || width < 1)
{
//task.resultInfo += $" 打标到大图坐标Rect(px):left={left},top={top}; width={width},height={height}\n";
//task.resultInfo += $" test point1.Y={point1.Y},h={DefectLabelInfoList[q].h}; top={top},mat.Height={task.bmp.Height}\n================\n";
WarningEvent?.Invoke(DateTime.Now, WarningEnum.Normal, task.resultInfo);
}
//保存
//string filename = $"{Config.appBasePath}\\temp\\{task.tag}\\{task.tag}_X{mmX.ToString()}_Y{mmY.ToString()}_W{mmW.ToString()}_H{mmH.ToString()}_目标{j + 1}_类别{classId}_置信度{confidence}.bmp";
//OpenCvSharp.Extensions.BitmapConverter.ToBitmap(new Mat(task.bmp, roi)).Save(filename, ImageFormat.Jpeg);
task.lstDefectBmp.Add(new Mat(task.bmpTag, roi).Clone());
liStep++;//7
}
#endregion
liStep = 99;
stopwatch.Stop();
task.stopwatch[3] = stopwatch.ElapsedMilliseconds;
task.isSucceed = true;
callback(task);
}
catch (Exception ex)
{
WarningEvent?.Invoke(DateTime.Now,WarningEnum.Low, $"DefectLib task3 err({liStep}):" + ex.Message);
task.isSucceed = false;
task.resultInfo = ex.Message;
callback(task);
}
Thread.Sleep(5);
}
}
private void callback(DefectTask task)
{
//返回成功/失败,异步调用
if (task.finishEvent != null || (task.finishEvent = finishEvent) != null)
//task.finishEvent.BeginInvoke(result, errInfo, res => task.finishEvent.EndInvoke(res), null);
System.Threading.ThreadPool.QueueUserWorkItem(waitCallback, task);
}
//异步回调
WaitCallback waitCallback = new WaitCallback(o =>
{
var task = (DefectTask)o;
task.finishEvent(task);
});
///
/// 切割(先左右,后上下)
///
///
///
///
///
private Mat[] OpenCVToCutsMat(Mat mat, int width, int height)
{
Mat[] array = new Mat[mat.Width / width * mat.Height / height];
int num = 0;
for (int i = 0; i < mat.Height / height; i++)
{
for (int j = 0; j < mat.Width / width; j++)
{
int x = j * width;
int y = i * height;
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(x, y, width, height);
Rect roi = new Rect(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
array[num] = new Mat(mat, roi).Clone();
num++;
}
}
return array;
}
public class DefectTask
{
public DefectTask()
{
// 创建一个 DataTable 对象来存储数据
excelTable = new DataTable("MyData");
// 添加列到 DataTable
excelTable.Columns.Add("FileName", typeof(string));
excelTable.Columns.Add("X", typeof(decimal));
excelTable.Columns.Add("Y", typeof(decimal));
excelTable.Columns.Add("W", typeof(decimal));
excelTable.Columns.Add("H", typeof(decimal));
excelTable.Columns.Add("目标", typeof(int));
excelTable.Columns.Add("类别", typeof(int));
excelTable.Columns.Add("置信度", typeof(decimal));
excelTable.Columns.Add("对比度", typeof(decimal));
// 向 DataTable 中添加数据行
//excelTable.Rows.Add("John Doe", 30);
//excelTable.Rows.Add("Jane Smith", 25);
}
///
/// 模型名字
///
public string modelName;
public Models.Records record;
//public string bmpPath;//源图路径(仅目录)
///
/// 源图(resize后的)
///
public Mat bmp;
//public Mat bmpBgr2rgb=new Mat();
public System.Drawing.Size resize = new System.Drawing.Size(224, 224);
//public Mat resizeBmp;//resize后 BGR2RGB图,只用于识别
public Mat bmpTag;
///
/// 图片索引0-n
///
public int photoIndex;//excel中对应的图像路径标识
public float widthRatio;//宽度比例,resize前/resize后
//切割后
public Mat[] bmps_cut;
//用于比对参数 }//置信度 面积 对比度
public List qualifiedLimitList;
//推理后结果用于打标
public float[] output;
public int[] output_num;
//
///
/// 完成后回调
///
public Action finishEvent;
//==结果返回
public bool isSucceed;//转换是否成功
public string resultInfo = "";//成功或失败信息
public List lstDefectBmp=new List();
///
/// fileIndex,x_mm,y_mm,w_mm,h_mm,目标,类别ID,置信度
///
public DataTable excelTable=new DataTable();
public long[] stopwatch = new long[4];
public int xw;
}
public void add(DefectTask task)
{
lock (taskList)
{
taskList.Add(task);
QueueCountEvent?.BeginInvoke(0, taskList.Count, null, null);
}
}
private DefectTask pop()
{
lock (taskList)
{
if (taskList.Count < 1)
return null;
//int index = 0;// taskList.FindIndex(p => { return p.isSync; });
//if (index < 0) index = 0;
var task = taskList[0];
taskList.RemoveAt(0);
QueueCountEvent?.BeginInvoke(0, taskList.Count, null, null);
return task;
}
}
private DefectTask pop2()
{
lock (taskOperationList)
{
if (taskOperationList.Count < 1)
return null;
//int index = 0;// taskList.FindIndex(p => { return p.isSync; });
//if (index < 0) index = 0;
var task = taskOperationList[0];
taskOperationList.RemoveAt(0);
QueueCountEvent?.BeginInvoke(1, taskOperationList.Count, null, null);
return task;
}
}
private DefectTask pop3()
{
lock (taskMakeTagList)
{
if (taskMakeTagList.Count < 1)
return null;
//int index = 0;// taskList.FindIndex(p => { return p.isSync; });
//if (index < 0) index = 0;
var task = taskMakeTagList[0];
taskMakeTagList.RemoveAt(0);
QueueCountEvent?.BeginInvoke(2, taskMakeTagList.Count, null, null);
return task;
}
}
public void Dispose()
{
stop();
}
}
}