效果

ONNX模型信息
facebook/dinov2-small

C#部署代码
cs
using System.Diagnostics;
using MathNet.Numerics.LinearAlgebra.Single;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenVinoSharp;
using Size = OpenCvSharp.Size;
namespace ImageSimilarityApp
{
public partial class Form1 : Form
{
private string imgPath1, imgPath2;
// OpenVINO 推理相关
private Core ieCore;
private Model model;
private CompiledModel compiledModel;
private InferRequest inferRequest;
// 预处理常量
private const int InputSize = 518;
public Form1()
{
InitializeComponent();
LoadModel();
}
private void LoadModel()
{
try
{
ieCore = new Core();
model = ieCore.read_model("dinov2_cls_norm.onnx");
compiledModel = ieCore.compile_model(model, "GPU.0");
inferRequest = compiledModel.create_infer_request();
lblResult.Text = "模型加载成功!";
}
catch (Exception ex)
{
MessageBox.Show($"模型加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(1);
}
}
private void BtnSelectImg1_Click(object sender, EventArgs e)
{
using OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "图像文件|*.jpg;*.jpeg;*.png;*.bmp";
if (dlg.ShowDialog() == DialogResult.OK)
{
imgPath1 = dlg.FileName;
picBox1.Image = Image.FromFile(imgPath1);
}
}
private void BtnSelectImg2_Click(object sender, EventArgs e)
{
using OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "图像文件|*.jpg;*.jpeg;*.png;*.bmp";
if (dlg.ShowDialog() == DialogResult.OK)
{
imgPath2 = dlg.FileName;
picBox2.Image = Image.FromFile(imgPath2);
}
}
private void BtnCompute_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(imgPath1) || string.IsNullOrEmpty(imgPath2))
{
MessageBox.Show("请先选择两张图片", "提示");
return;
}
try
{
Stopwatch sw = Stopwatch.StartNew();
float[] feat1 = ExtractFeature(imgPath1);
float[] feat2 = ExtractFeature(imgPath2);
sw.Stop();
long time1= sw.ElapsedMilliseconds;
sw.Restart();
double similarity = CosineSimilarity(feat1, feat2);
sw.Stop();
long time2 = sw.ElapsedMilliseconds;
lblResult.Text = $"相似度: {similarity:F6} 推理耗时: {time1} ms 相似度计算耗时: {time2} ms ";
}
catch (Exception ex)
{
MessageBox.Show($"计算失败: {ex.Message}", "错误");
}
}
private float[] ExtractFeature(string imagePath)
{
// 1. 读取并预处理图像
using Mat img = Cv2.ImRead(imagePath);
if (img.Empty())
throw new Exception("图像读取失败");
// 转为 RGB
Mat rgbImg = new Mat();
Cv2.CvtColor(img, rgbImg, ColorConversionCodes.BGR2RGB);
// resize
Mat resized = new Mat();
Cv2.Resize(rgbImg, resized, new Size(InputSize, InputSize), 0, 0, InterpolationFlags.Linear);
// 转 float32 并归一化到 [0,1]
resized.ConvertTo(resized, MatType.CV_32FC3, 1.0 / 255.0);
// 标准化 (mean, std)
Mat normImg = new Mat();
Cv2.Subtract(resized, new Scalar(0.485f, 0.456f, 0.406f), normImg);
Cv2.Divide(normImg,new Scalar(0.229f, 0.224f, 0.225f),normImg);
// CHW 布局 (OpenVINO 默认 NCHW)
Mat chwImg = CvDnn.BlobFromImage(normImg); // 返回 1x3xHxW
// 获取输入张量
Tensor inputTensor = inferRequest.get_input_tensor();
inputTensor.shape = new Shape([1, 3, 518, 518]);
// 将数据拷贝到输入张量
float[] inputData = new float[1 * 3 * InputSize * InputSize];
System.Runtime.InteropServices.Marshal.Copy(chwImg.Data, inputData, 0, inputData.Length);
inputTensor.set_data(inputData);
// 推理
inferRequest.infer();
// 获取输出张量 (归一化特征)
Tensor outputTensor = inferRequest.get_output_tensor();
float[] feature = outputTensor.get_data<float>((int)outputTensor.size);
// feature 已经是 L2 归一化 (shape: 1x384)
float[] feat = new float[384];
Array.Copy(feature, 0, feat, 0, 384);
return feat;
}
private float CosineSimilarity(float[] a, float[] b)
{
// 因为向量的 L2 范数已经是 1 (模型输出已归一化),所以直接返回点积
float dot = (float)System.Numerics.Tensors.TensorPrimitives.CosineSimilarity(a,b);
return dot;
}
private float CosineSimilarity2(float[] a, float[] b)
{
var vector1 = new DenseVector(a);
var vector2 = new DenseVector(b);
float dotProduct = vector1.DotProduct(vector2);
//float magnitude = (float)(vector1.L2Norm() * vector2.L2Norm());
//float similarity = magnitude == 0 ? 0 : dotProduct / magnitude;
float similarity = dotProduct;
return similarity;
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
inferRequest?.Dispose();
compiledModel?.Dispose();
model?.Dispose();
ieCore?.Dispose();
base.OnFormClosed(e);
}
}
}