C# OpenvinoSharp使用Anomalib的PatchCore算法进行缺陷检测
本文介绍了一个基于OpenVINO的异常检测系统实现,主要针对MVTecDataset的bottle类进行检测。系统采用C# WinForms开发,包含图像加载、预处理、模型推理和结果显示等功能模块。通过OpenVINO运行时加载PatchCore ONNX模型,实现了256x256分辨率输入的异常检测,可输出异常分数、预测标签、异常热力图和预测掩码。系统界面显示原始图像、异常热力图和预测掩码,并
检测效果如下(模型数据集为MVTecDataset的bottle类):

模型信息如下:

///Form2.Designer.cs
using System.Drawing;
using System.Windows.Forms;
using System;
namespace yolo_world_opencvsharp_net4._8
{
partial class Form2
{
private System.ComponentModel.IContainer components = null;
private PictureBox originalImageBox;
private PictureBox anomalyMapBox;
private PictureBox predMaskBox;
private Button loadImageButton;
private Button inferenceButton;
private Label scoreLabel;
private Label labelLabel;
private ProgressBar progressBar;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.originalImageBox = new System.Windows.Forms.PictureBox();
this.anomalyMapBox = new System.Windows.Forms.PictureBox();
this.predMaskBox = new System.Windows.Forms.PictureBox();
this.loadImageButton = new System.Windows.Forms.Button();
this.inferenceButton = new System.Windows.Forms.Button();
this.scoreLabel = new System.Windows.Forms.Label();
this.labelLabel = new System.Windows.Forms.Label();
this.progressBar = new System.Windows.Forms.ProgressBar();
this.elapsedTimeLabel = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.originalImageBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.anomalyMapBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.predMaskBox)).BeginInit();
this.SuspendLayout();
//
// originalImageBox
//
this.originalImageBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.originalImageBox.Location = new System.Drawing.Point(20, 18);
this.originalImageBox.Name = "originalImageBox";
this.originalImageBox.Size = new System.Drawing.Size(256, 236);
this.originalImageBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.originalImageBox.TabIndex = 0;
this.originalImageBox.TabStop = false;
//
// anomalyMapBox
//
this.anomalyMapBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.anomalyMapBox.Location = new System.Drawing.Point(300, 18);
this.anomalyMapBox.Name = "anomalyMapBox";
this.anomalyMapBox.Size = new System.Drawing.Size(256, 236);
this.anomalyMapBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.anomalyMapBox.TabIndex = 1;
this.anomalyMapBox.TabStop = false;
//
// predMaskBox
//
this.predMaskBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.predMaskBox.Location = new System.Drawing.Point(580, 18);
this.predMaskBox.Name = "predMaskBox";
this.predMaskBox.Size = new System.Drawing.Size(256, 236);
this.predMaskBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.predMaskBox.TabIndex = 2;
this.predMaskBox.TabStop = false;
//
// loadImageButton
//
this.loadImageButton.Location = new System.Drawing.Point(20, 277);
this.loadImageButton.Name = "loadImageButton";
this.loadImageButton.Size = new System.Drawing.Size(100, 28);
this.loadImageButton.TabIndex = 3;
this.loadImageButton.Text = "加载图像";
this.loadImageButton.Click += new System.EventHandler(this.LoadImageButton_Click);
//
// inferenceButton
//
this.inferenceButton.Enabled = false;
this.inferenceButton.Location = new System.Drawing.Point(140, 277);
this.inferenceButton.Name = "inferenceButton";
this.inferenceButton.Size = new System.Drawing.Size(100, 28);
this.inferenceButton.TabIndex = 4;
this.inferenceButton.Text = "开始推理";
this.inferenceButton.Click += new System.EventHandler(this.InferenceButton_Click);
//
// scoreLabel
//
this.scoreLabel.Location = new System.Drawing.Point(20, 323);
this.scoreLabel.Name = "scoreLabel";
this.scoreLabel.Size = new System.Drawing.Size(300, 18);
this.scoreLabel.TabIndex = 5;
this.scoreLabel.Text = "异常分数: ";
//
// labelLabel
//
this.labelLabel.Location = new System.Drawing.Point(20, 351);
this.labelLabel.Name = "labelLabel";
this.labelLabel.Size = new System.Drawing.Size(300, 18);
this.labelLabel.TabIndex = 6;
this.labelLabel.Text = "预测标签: ";
//
// progressBar
//
this.progressBar.Location = new System.Drawing.Point(20, 388);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(800, 18);
this.progressBar.TabIndex = 7;
this.progressBar.Visible = false;
//
// elapsedTimeLabel
//
this.elapsedTimeLabel.AutoSize = true;
this.elapsedTimeLabel.Location = new System.Drawing.Point(580, 292);
this.elapsedTimeLabel.Name = "elapsedTimeLabel";
this.elapsedTimeLabel.Size = new System.Drawing.Size(41, 12);
this.elapsedTimeLabel.TabIndex = 8;
this.elapsedTimeLabel.Text = "耗时:";
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(864, 426);
this.Controls.Add(this.elapsedTimeLabel);
this.Controls.Add(this.originalImageBox);
this.Controls.Add(this.anomalyMapBox);
this.Controls.Add(this.predMaskBox);
this.Controls.Add(this.loadImageButton);
this.Controls.Add(this.inferenceButton);
this.Controls.Add(this.scoreLabel);
this.Controls.Add(this.labelLabel);
this.Controls.Add(this.progressBar);
this.Name = "Form2";
this.Text = "AnomalibSeg 异常检测";
((System.ComponentModel.ISupportInitialize)(this.originalImageBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.anomalyMapBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.predMaskBox)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
private Label elapsedTimeLabel;
}
}
///Form2.cs
using OpenCvSharp;
using OpenCvSharp.Extensions;
using OpenVinoSharp;
using OpenVinoSharp.Extensions.process;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Size = OpenCvSharp.Size;
namespace yolo_world_opencvsharp_net4._8
{
public partial class Form2 : Form
{
private Core core;
private Model model;
private CompiledModel compiledModel;
private InferRequest inferRequest;
private string imagePath;
private Mat originalImage;
private Mat processedImage;
private const int INPUT_WIDTH = 256;
private const int INPUT_HEIGHT = 256;
private const string MODEL_PATH = @"D:\3D\patchcore.onnx";
public Form2()
{
InitializeComponent();
InitializeModel();
}
private void InitializeModel()
{
try
{
progressBar.Visible = true;
progressBar.Style = ProgressBarStyle.Marquee;
// 初始化OpenVINO运行时
core = new Core();
// 加载模型
model = core.read_model(MODEL_PATH);
// 编译模型
compiledModel = core.compile_model(model, "CPU");
// 创建推理请求
inferRequest = compiledModel.create_infer_request();
progressBar.Visible = false;
MessageBox.Show("模型加载成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
progressBar.Visible = false;
MessageBox.Show($"模型加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadImageButton_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "图像文件|*.jpg;*.jpeg;*.png;*.bmp";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
imagePath = openFileDialog.FileName;
originalImage = new Mat(imagePath);
// 显示原图
originalImageBox.Image = MatToBitmap(originalImage);
inferenceButton.Enabled = true;
}
}
}
private void InferenceButton_Click(object sender, EventArgs e)
{
if (originalImage == null)
{
MessageBox.Show("请先加载图像!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
try
{
progressBar.Visible = true;
progressBar.Style = ProgressBarStyle.Marquee;
Stopwatch sw = Stopwatch.StartNew();
// 预处理图像
processedImage = PreprocessImage(originalImage);
// 执行推理
var results = PerformInference(processedImage);
sw.Stop();
elapsedTimeLabel.Text = sw.ElapsedMilliseconds.ToString() + "ms";
// 显示结果
DisplayResults(results);
progressBar.Visible = false;
}
catch (Exception ex)
{
progressBar.Visible = false;
MessageBox.Show($"推理失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private Mat PreprocessImage(Mat image)
{
// 调整大小
Mat resized = new Mat();
Cv2.Resize(image, resized, new Size(INPUT_WIDTH, INPUT_HEIGHT));
// 转换颜色空间 BGR -> RGB
Mat rgb = new Mat();
Cv2.CvtColor(resized, rgb, ColorConversionCodes.BGR2RGB);
// 归一化 [0,255] -> [0,1]
Mat normalized = new Mat();
rgb.ConvertTo(normalized, MatType.CV_32FC3, 1.0 / 255.0);
rgb.Dispose();
resized.Dispose();
return normalized;
}
private InferenceResults PerformInference(Mat input)
{
// 获取输入张量
using (Tensor inputTensor = inferRequest.get_input_tensor(0))
{
// 准备输入数据
float[] inputData = Permute.run(input);
// 设置输入数据
inputTensor.set_shape(new Shape(1, 3, 256, 256));
inputTensor.set_data(inputData);
// 执行推理
inferRequest.infer();
}
// 获取输出结果
using (Tensor predScoreTensor = inferRequest.get_tensor("pred_score"))
using (Tensor predLabelTensor = inferRequest.get_tensor("pred_label"))
using (Tensor anomalyMapTensor = inferRequest.get_tensor("anomaly_map"))
using (Tensor predMaskTensor = inferRequest.get_tensor("pred_mask"))
{
//var PredScore = predScoreTensor.get_data<float>((int)predScoreTensor.get_size());
//var PredLabel = predLabelTensor.get_data<byte>((int)predLabelTensor.get_size());
//var AnomalyMap = anomalyMapTensor.get_data<float>((int)anomalyMapTensor.get_size());
//var PredMask = predMaskTensor.get_data<byte>((int)predMaskTensor.get_size());
return new InferenceResults
{
PredScore = predScoreTensor.get_data<float>((int)predScoreTensor.get_size()),
PredLabel = predLabelTensor.get_data<byte>((int)predLabelTensor.get_size()),
AnomalyMap = anomalyMapTensor.get_data<float>((int)anomalyMapTensor.get_size()),
PredMask = predMaskTensor.get_data<byte>((int)predMaskTensor.get_size())
};
}
}
private void DisplayResults(InferenceResults results)
{
// 显示异常分数和预测标签
scoreLabel.Text = $"异常分数: {results.PredScore[0]:F4}";
labelLabel.Text = $"预测标签: {(results.PredLabel[0]>0 ? "异常" : "正常")}";
// 处理异常图
if (results.AnomalyMapDims != null && results.AnomalyMapDims.Length >= 2)
{
int height = (int)results.AnomalyMapDims[1];
int width = (int)results.AnomalyMapDims[2];
Mat anomalyMap = new Mat(height, width, MatType.CV_32FC1, results.AnomalyMap);
Mat anomalyMapColored = ApplyColorMap(anomalyMap);
anomalyMapBox.Image = MatToBitmap(anomalyMapColored);
anomalyMap.Dispose();
anomalyMapColored.Dispose();
}
// 处理预测掩码
if (results.PredMaskDims != null && results.PredMaskDims.Length >= 2)
{
int height = (int)results.PredMaskDims[1];
int width = (int)results.PredMaskDims[2];
Mat predMask = new Mat(height, width, MatType.CV_8UC1);
byte[] maskData = new byte[results.PredMask.Length];
for (int i = 0; i < results.PredMask.Length; i++)
{
maskData[i] = results.PredMask[i];
}
predMask.SetArray(maskData);
predMaskBox.Image = MatToBitmap(predMask*255);
predMask.Dispose();
}
}
private Mat ApplyColorMap(Mat anomalyMap)
{
// 归一化到[0,1]
Mat normalized = new Mat();
Cv2.Normalize(anomalyMap, normalized, 0, 1, NormTypes.MinMax);
// 转换为8位
Mat u8 = new Mat();
normalized.ConvertTo(u8, MatType.CV_8UC1, 255);
// 应用Jet色彩映射
Mat colored = new Mat();
Cv2.ApplyColorMap(u8, colored, ColormapTypes.Jet);
normalized.Dispose();
u8.Dispose();
return colored;
}
private Bitmap MatToBitmap(Mat mat)
{
if (mat.Channels() == 1)
{
return mat.ToBitmap();
}
else
{
// 确保颜色顺序正确 (OpenCV使用BGR,.NET使用RGB)
Mat rgb = new Mat();
Cv2.CvtColor(mat, rgb, ColorConversionCodes.BGR2RGB);
Bitmap bitmap = rgb.ToBitmap();
rgb.Dispose();
return bitmap;
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
// 清理资源
originalImage?.Dispose();
processedImage?.Dispose();
inferRequest?.Dispose();
compiledModel?.Dispose();
model?.Dispose();
core?.Dispose();
base.OnFormClosing(e);
}
}
// 推理结果数据结构
public class InferenceResults
{
public float[] PredScore { get; set; }
public byte[] PredLabel { get; set; }
public float[] AnomalyMap { get; set; }
public byte[] PredMask { get; set; }
// 维度信息(根据实际模型输出调整)
public long[] AnomalyMapDims => new long[] { 1, 256, 256, 1 }; // 示例维度
public long[] PredMaskDims => new long[] { 1, 256, 256, 1 }; // 示例维度
}
}
更多推荐


所有评论(0)