以下是一个用C#编写的winforms窗体程序,其主要功能在于产品的logo和条形码检测,我会分文件发送代码段给你,请详细给出代码的解释:
MainForm.cs:/**
* 依赖: VS2017~、.net sdk 4.6.2、DevExpress21.2.7
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using AForge.Video;
using ChenaoboSharpLib;
using Timer = System.Windows.Forms.Timer;
using System.Linq;
namespace LogoTest
{
public partial class MainForm : Form
{
private readonly SystemInfoProvider _systemInfoProvider = new SystemInfoProvider(); // 系统信息
private readonly JsonConfigManager _cfgManager = new JsonConfigManager("config.json"); // 配置文件
private readonly Timer _systemTimer = new Timer() { Interval = 1000 }; // 每秒更新一次时间
private TplinkMvCamera _mvCamera; // 工业相机用作logo检测
private UvcCamera _uvcCamera = new UvcCamera(); // USB摄像头用作SN扫码
private List<string> _selectedLabels; // 选择的要识别的logo类型
private List<string> _detectedDistinctLabels; // 存放检测到的logo类别名称
private Dictionary<int, string> _modelLabelId_to_logoName; // 中英文映射
private string _modelName; // 模型名称
private Yolo11OpenVino _detector1; // 目标检测器
private BarcodeDecoder _barCodeDetector = new BarcodeDecoder(); // 条形码检测器
private string oldSN = "0000000000000"; // 上一次的SN
private volatile bool _snDecoding = false; // SN解码中标志
private float reTestDelay = 0; // 自动重测延时
private volatile int reTestTimes = 0; // 重测次数
private readonly TTS _tts = new TTS(100, 100); // 文本转语音
private SerialPortTransceiver _serialPortTransceiver; // 串口收发器
private string _comNum = null; // 串口号
private readonly List<CommonLib.Log.LogType> logs = new List<CommonLib.Log.LogType>(); // 日志
private readonly int logMaxCount = 1000; // 日志长度
// 构造函数
public MainForm()
{
// 初始化UI
InitializeComponent();
// 设定UI初始状态(和在设计器中设置效果是一样的)
barButtonItemAutoGainAndExposure.Enabled = false;
barEditItemGain.Enabled = false;
barEditItemExposure.Enabled = false;
// 注册回调
Load += new EventHandler(MainForm_Load); // 窗口加载
FormClosing += new FormClosingEventHandler(MainForm_FormClosing); // 串口关闭
PictureBox_Preview.DoubleClick += PictureBox_DoubleClick;
PictureBox_Result.DoubleClick += PictureBox_DoubleClick;
pictureBoxUsb.DoubleClick += PictureBox_DoubleClick;
barEditRetestTime.EditValueChanged += change_retestTime;
barEditItemGain.EditValueChanged += ChangeMvCameraCfg;
barEditItemExposure.EditValueChanged += ChangeMvCameraCfg;
// 定时器回调
_systemTimer.Tick += SystemTimer_Tick;
}
// 系统定时器回调1,每1s进入一次,显示时间
private void SystemTimer_Tick(object sender, EventArgs e)
{
base.Invoke(new Action(delegate //UI线程
{
labelRealTime.Text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}";
labelCpuLoad.Text = $"CPU: {_systemInfoProvider.GetCpuUsage():F1}%";
labelRamLoad.Text = $"RAM: {_systemInfoProvider.GetRamUsageDetails()}";
}));
}
// 加载assets中的模型到下拉列表
private void LoadModels()
{
comboxModels.Items.Clear();//清空
// 加载模型文件名称到下拉列表
string assetsFolderPath = Path.Combine(Application.StartupPath, "assets");
if (Directory.Exists(assetsFolderPath))
{
string[] xmlFiles = Directory.GetFiles(assetsFolderPath, "*.xml");
foreach (string file in xmlFiles)
{
comboxModels.Items.Add(Path.GetFileName(file));
}
}
else
{
WinFormsUtils.ShowTipDialog("assets文件夹不存在! 请联系管理员");
return;
}
}
// 窗口加载回调
private void MainForm_Load(object sender, EventArgs e)
{
Text = "LogoTest —— 吸顶AP上盖Logo检测工具 V1.0.5";
labelCpuModel.Text = _systemInfoProvider.GetCpuModel(); //获取cpu型号
labelCpuLoad.Text = $"CPU: {_systemInfoProvider.GetCpuUsage():F1}%";
labelRamLoad.Text = $"RAM: {_systemInfoProvider.GetRamUsageDetails()}";
gridControl1.DataSource = logs; // 日志数据源
// 显示串口列表
repositoryItemComboBox5.Items.Clear();
string[] coms = System.IO.Ports.SerialPort.GetPortNames();
foreach (var item in coms)
{
repositoryItemComboBox5.Items.Add(item);
}
LoadModels(); //加载模型列表
// logo选项及其预览图片
var dict = new Dictionary<string, string>();
var imageFiles = Directory.GetFiles("assets")
.Where(f => Path.GetExtension(f).Equals(".png", StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(f).Equals(".jpg", StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(f).Equals(".jpeg", StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(f).Equals(".bmp", StringComparison.OrdinalIgnoreCase))
.ToArray();
foreach (var file in imageFiles)
{
var fileName = Path.GetFileName(file); // 获取文件名(含扩展名)
var key = Path.GetFileNameWithoutExtension(file); // 移除扩展名,作为字典键
if (!string.IsNullOrEmpty(key))
{
dict[key] = "assets\\" + fileName;
}
}
new WinFormsUtils.CheckListBoxWithImagePreview(checkedListBox1, dict);
// 模型label与logo选项名称的对应关系(模型的label为英文)
_modelLabelId_to_logoName = new Dictionary<int, string>();
_modelLabelId_to_logoName.Add(0, "TP-LINK");
_modelLabelId_to_logoName.Add(1, "中国电信");
_modelLabelId_to_logoName.Add(2, "TP-LINK凹印");
_modelLabelId_to_logoName.Add(3, "ChinaMobile");
_modelLabelId_to_logoName.Add(4, "域联");
_modelLabelId_to_logoName.Add(5, "MERCURY");
//加载配置
if (_cfgManager.KeyExists("modelName"))
barEditItemSelectModel.EditValue = _cfgManager.GetValue<string>("modelName");
if (_cfgManager.KeyExists("comNum"))
barEditItem3.EditValue = _cfgManager.GetValue<string>("comNum");
if (_cfgManager.KeyExists("reTestGap"))
barEditRetestTime.EditValue = _cfgManager.GetValue<string>("reTestGap");
if (_cfgManager.KeyExists("selectLogos"))
{
var selectedLogs = new List<string>();
selectedLogs = _cfgManager.GetValue<List<string>>("selectLogos");
foreach (string log in selectedLogs)
{
for (int i = 0; i < checkedListBox1.Items.Count; i++)
{
if (log == checkedListBox1.Items[i].ToString())
{
checkedListBox1.SetItemChecked(i, true);
}
}
}
}
// 开启系统定时器
_systemTimer.Start();
}
//点击开始
private void BarButtonItem_Start_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
reTestDelay = Convert.ToSingle(barEditRetestTime.EditValue);
reTestTimes = 0;
if (checkedListBox1.CheckedItems.Count < 1)//已选logo类型数量
{
WinFormsUtils.ShowTipDialog("请先选择logo元素");
return;
}
_selectedLabels = new List<string>();
foreach (var item in checkedListBox1.CheckedItems)
{
_selectedLabels.Add(item.ToString());
}
if (barCheckItem_com.Checked)
{
_comNum = barEditItem3.EditValue as string;
if (_comNum == null)
{
WinFormsUtils.ShowTipDialog("开启了串口输出,但是未选择串口");
return;
}
}
_serialPortTransceiver = new SerialPortTransceiver(_comNum);//串口发送SN 波特率9600
_serialPortTransceiver.Open();
if (barEditItemSelectModel.EditValue == null)
{
WinFormsUtils.ShowTipDialog("未选择AI模型");
return;
}
_modelName = $"{barEditItemSelectModel.EditValue}";
Trace.WriteLine(">>>初始化条码解码器");
_barCodeDetector = new BarcodeDecoder(barCheckItemTryHarder.Checked);
Thread.Sleep(100); // 不加延时会概率性不解码
Trace.WriteLine(">>>初始化工业相机");
_mvCamera = new TplinkMvCamera("TPLINK_MV_CAMERA_SDK");
if (!_mvCamera.ListCamera()) return;
if (!_mvCamera.ConnectCameraAndStart()) return;
_mvCamera.PictureboxPreview(PictureBox_Preview, 20); //预览
barButtonItemAutoGainAndExposure_ItemClick(null, null);
try
{
Trace.WriteLine(">>>初始化USB相机");
_uvcCamera = new UvcCamera();//USB摄像头用作SN检测
_uvcCamera.Connect(0);
_uvcCamera.FrameCaptured += UsbCamera_ImageGrabbed;
}
catch (Exception error)
{
WinFormsUtils.ShowTipDialog(error.ToString());
}
_detector1 = new Yolo11OpenVino($"assets\\{_modelName}", 0.7f, 0.5f);
//UI
SwitchUI(true);
//创建目录
if (!Directory.Exists("results"))
{
Directory.CreateDirectory("results");
}
// 保存配置
_cfgManager.SetValue("modelName", barEditItemSelectModel.EditValue.ToString());
_cfgManager.SetValue("comNum", barEditItem3.EditValue.ToString());
_cfgManager.SetValue("reTestGap", barEditRetestTime.EditValue.ToString());
_cfgManager.SetValue("selectLogos", _selectedLabels);
}
//修改相机参数
private void ChangeMvCameraCfg(object sender, EventArgs e)
{
var edit = sender as DevExpress.XtraBars.BarEditItem;
if (edit != null)
{
var value = Convert.ToDouble(edit.EditValue);
switch (edit.Name)
{
case "barEditItemGain":
_mvCamera.ChangeGain((float)value);
break;
case "barEditItemExposure":
_mvCamera.ChangeExposureTime((float)value * 1000); //ms转us
break;
}
}
}
//UI切换
private void SwitchUI(bool startClicked)
{
base.Invoke(new Action(delegate //UI线程
{
//点开始后才可以点的
BarButtonItem_Stop.Enabled = startClicked;
barButtonItemReTestOnce.Enabled = startClicked;
barButtonItemAutoGainAndExposure.Enabled = startClicked;
barEditItemGain.Enabled = startClicked;
barEditItemExposure.Enabled = startClicked;
//点开始后不可点的
barStaticItem1.Enabled = !startClicked;
barCheckItem2.Enabled = !startClicked;
barCheckItem_com.Enabled = !startClicked;
barEditItem3.Enabled = !startClicked;
BarButtonItem_Start.Enabled = !startClicked;
checkedListBox1.Enabled = !startClicked;
barEditRetestTime.Enabled = !startClicked;
barCheckRetest.Enabled = !startClicked;
barStaticItem3.Enabled = !startClicked;
barEditItemSelectModel.Enabled = !startClicked;
}));
}
//stop按钮回调
private void BarButtonItem_Stop_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
_mvCamera.StopAndDisconnectCameraAndDestroySdk();
try
{
_uvcCamera.Dispose(); // 销毁资源
}
catch (Exception error)
{
WinFormsUtils.ShowTipDialog(error.ToString());
}
_serialPortTransceiver.Dispose();
SwitchUI(false);
oldSN = "0000000000000";
}
//关闭窗口回调
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (BarButtonItem_Stop.Enabled) //如果未停止,先停止
{
BarButtonItem_Stop_ItemClick(null, null);
}
}
//USB摄像头取图回调
private void UsbCamera_ImageGrabbed(object sender, NewFrameEventArgs e)
{
Image snImage = new Bitmap(e.Frame) as Image; // 必须复制,否则报错
// UI
base.Invoke(new Action(delegate
{
pictureBoxUsb.Image = snImage;
}));
if (!_snDecoding)
{
_snDecoding = true; //防止重复进入
string SN = _barCodeDetector.Detect(ref snImage);//解码
// UI
base.Invoke(new Action(delegate
{
labelSn.Text = SN;
pictureBoxUsb.Image = snImage; //显示红线
}));
if (SN != oldSN //是新SN,说明放上去新产品
&& SN.Length == 13 && System.Text.RegularExpressions.Regex.IsMatch(SN, "1[1-3][0-9][0-9ABC][0-9A-Z]{9}")) // SN筛选规则
{
oldSN = SN;
reTestTimes = 0;
TestMain(SN);
}
_snDecoding = false;
}
}
//双击全屏显示画面
private void PictureBox_DoubleClick(object sender, EventArgs e)
{
var pictureBox = sender as PictureBox;
if (pictureBox != null && pictureBox.Image != null)
{
WinFormsUtils.FullScreenImageForm fullScreenForm = new WinFormsUtils.FullScreenImageForm(pictureBox.Image);
fullScreenForm.Show();
}
}
//测试主体
private void TestMain(string SN)
{
Bitmap bmp = _mvCamera.GetCurrentFrame();
Mat srcImg = OpenCvUtils.BitmapToBgrMat(bmp);
#region logo检测
_detectedDistinctLabels = new List<string>();
OnePicResults yoloResult = _detector1.InferenceMat(srcImg, false);
Mat result_image = yoloResult.ResultImg; //推理后的画框图像
var detResults = yoloResult.DetResults; //检测结果列表
foreach (var r in detResults) //绘制模型1检测框
{
Scalar color = _detector1.labelColors[r.LabelId];
Cv2.PutText(result_image, $"{r.LabelName} {r.Confidence:F2}",
new OpenCvSharp.Point(r.Rect.TopLeft.X, r.Rect.TopLeft.Y + 10),
HersheyFonts.HersheySimplex, 4, color, thickness: 4);
Cv2.Rectangle(result_image, r.Rect, color, thickness: 4);
// 检测标签id转logo名称
if (_modelLabelId_to_logoName.TryGetValue(r.LabelId, out string detectedLogo))
{
// 将检测到的logo类别添加到distinct列表
if (!_detectedDistinctLabels.Contains(detectedLogo))
{
_detectedDistinctLabels.Add(detectedLogo);
}
}
}
Trace.WriteLine($">>>检测到的logo类别: {String.Join(", ", _detectedDistinctLabels.ToArray())}");
bool result = true;
foreach (string label in _detectedDistinctLabels)
{
if (!_selectedLabels.Contains(label))//distinctLabels没有都包含在已选logo类别中,也就是多了
{
result = false;
}
}
if (_selectedLabels.Count != _detectedDistinctLabels.Count)//distinctLabels数量和已选logo类别数量不符,也就是少了
{
result = false;
}
#endregion
#region 测试结果处理
//串口发送PASS的SN
if (barCheckItem_com.Checked)
{
#if !DEBUG
if (result)
#else
if (true)
#endif
{
Task.Run(() =>
{
try
{
_serialPortTransceiver.Send(SN+"\r\n"); // 带换行
Thread.Sleep(Convert.ToInt32(barEditItemComSleep.EditValue)); // 延时 TODO:测试
}
catch (Exception error)
{
WinFormsUtils.ShowTipDialog(error.ToString());
}
});
}
}
//语音播报测试结果
Color resultColor = result ? Color.Green : Color.Red;
string resultStr = result ? "PASS" : "FAIL";
if (barCheckItem2.Checked)
_tts.Read(result ? "合格" : "测试不合格,请重新测试");
//保存图片
string tempPath = Path.Combine(Application.StartupPath, "results");
string addedPath = string.Empty;
for (int ind = 0; ind < _selectedLabels.Count; ind++)
{
addedPath += _selectedLabels[ind].ToString();
if (!(ind == _selectedLabels.Count - 1))
{
addedPath += ",";
}
}
if (!Directory.Exists(Path.Combine(Path.Combine(tempPath, addedPath), resultStr)))
{
Directory.CreateDirectory(Path.Combine(Path.Combine(tempPath, addedPath), resultStr));
}
String filename = Path.Combine(tempPath, addedPath, resultStr, DateTime.Now.ToString("yyyyMMdd_HHmmss_") + SN + ".jpg");
Cv2.ImWrite(filename, result_image);
//保存log
string selectedLogoStr = addedPath;
string detectedLogoStr = String.Join(",", _detectedDistinctLabels);
string logStr = string.Format("{0}\t{1}\t{2}\t选择的logo={3}\t检测到的logo={4}", SN, resultStr, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), selectedLogoStr, detectedLogoStr);
CommonLib.Log.WriteResult(logStr);//写入日志文件
if (logs.Count > logMaxCount)
{
logs.RemoveAt(logMaxCount);
}
logs.Insert(0, new CommonLib.Log.LogType //TODO 增加日志:实际检测到的logo,需要检测的logo,用序号1~10显示
{
SN = SN,
TestTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
Result = resultStr,
SelectedLogo = selectedLogoStr,
DetectedLogo = detectedLogoStr
});
#endregion
//UI
base.Invoke(new Action(delegate
{
PictureBox_Result.Image = BitmapConverter.ToBitmap(result_image);//AI结果图像显示
Label_Result.ForeColor = resultColor;
Label_Result.Text = resultStr;
gridControl1.RefreshDataSource();//显示日志
}));
//FAIL自动重测
if (!result && barCheckRetest.Checked && reTestTimes < 1) //自动重测一次
{
reTestTimes += 1;
Thread.Sleep((int)(reTestDelay * 1000)); //自动重测延时
TestMain(oldSN);
}
}
//复杂解码选项改变
private void barCheckItem1_CheckedChanged(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
if (barCheckItemTryHarder.Checked)
{
_barCodeDetector = new BarcodeDecoder(true);
}
else
{
_barCodeDetector = new BarcodeDecoder(false);
}
}
//文本框颜色控制
private void gridView1_RowCellStyle(object sender, DevExpress.XtraGrid.Views.Grid.RowCellStyleEventArgs e)
{
if (e.RowHandle == 0)
{
e.Appearance.BackColor = Color.Yellow;
}
if (gridView1.GetRowCellValue(e.RowHandle, gridColumn3).ToString() == "FAIL")
{
e.Appearance.BackColor = Color.PaleVioletRed;
}
}
#if !DEBUG
//监听热键
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.M)
{
barButtonItemReTestOnce.PerformClick();
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
#endif
//重测延时调整
private void change_retestTime(object sender, EventArgs e)
{
reTestDelay = Convert.ToSingle(barEditRetestTime.EditValue);
}
//点击重测一次
private void barButtonItem1_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
reTestTimes = 1;
TestMain(oldSN);
}
//显示日志文件夹
private void barButtonItem3_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Process.Start("explorer", $"/n, {System.Environment.CurrentDirectory}\\results");
}
//关于
private void barButtonItem5_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Task.Run(() =>
{
string url;
if (pingHost("10.192.249.146"))
{
url = "http://10.192.249.146:8090/pages/viewpage.action?pageId=157548787";
}
else
{
url = "https://docs.tp-manu.com.cn/pages/viewpage.action?pageId=157548787";
}
Process.Start(url);
});
}
//检测ping通
private bool pingHost(string host)
{
Trace.WriteLine($">>>Ping:{host}");
bool result = false;
System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping();
try
{
System.Net.NetworkInformation.PingReply reply = pinger.Send(host);
if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
{
result = true;
}
}
catch (System.Net.NetworkInformation.PingException error)
{
WinFormsUtils.ShowTipDialog(error.ToString());
return false;
}
Trace.WriteLine($">>>Ping result:{result}");
return result;
}
//刷新串口列表
private void barStaticItem1_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
Trace.WriteLine(">>>Refresh COM list");
//刷新串口列表
repositoryItemComboBox5.Items.Clear();
string[] coms = System.IO.Ports.SerialPort.GetPortNames();
foreach (var item in coms)
{
repositoryItemComboBox5.Items.Add(item);
}
WinFormsUtils.ShowTipDialog("串口列表已刷新!");
}
// 下载模型
private void barButtonItemRefreshModels_ItemClick_1(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
WinFormsUtils.ShowTipDialog("将从远端服务器下载模型文件, 并更新模型下拉选择列表。\r\n这将花一定时间...点击确定继续");
string remoteHost;
if (NetworkUtils.PingHost("10.192.249.146"))
remoteHost = "10.192.249.146";
else
remoteHost = "172.18.100.58";
SshUtils.DownloadFilesToLocal(remoteHost, "root", "Tplink123", "/home/tp/containers/remoteAssets", "assets");
LoadModels();
}
// 自动增益曝光
private void barButtonItemAutoGainAndExposure_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
_mvCamera.AutoConfigOn();
Thread.Sleep(1000); // 等待自动调整完毕
_mvCamera.AutoConfigOff();
barEditItemGain.EditValue = _mvCamera.GetGain();
barEditItemExposure.EditValue = _mvCamera.GetExposureTime() / 1000;
}
}
}
最新发布