C# OpenvinoSharp使用DINOv2模型进行图像相似度计算

效果

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);
        }
    }
}
相关推荐
DFT计算杂谈2 小时前
自动化脚本一键绘制三元化合物相图
java·运维·服务器·开发语言·前端·python·自动化
EW Frontier2 小时前
6G ISAC新范式:基于智能漏波天线的Wi‑Fi通感一体化系统设计与实测【附MATLAB+python代码】
开发语言·python·matlab·music·isac·doa·wi‑fi
楼田莉子2 小时前
Linux网络:NAT_代理
linux·运维·服务器·开发语言·c++·后端
froginwe112 小时前
jEasyUI 创建基础树形网格
开发语言
月昤昽3 小时前
autoCAD二次开发 4.正多边形与collection区分
算法·c#·二次开发·autocad二次开发
Victory_20253 小时前
c#定时器顺序控制写法
开发语言·c#·c#顺序控制+定时器
Cyber4K3 小时前
【Python专项】Nginx访问日志分析时间范围处理示例
开发语言·python·nginx
中犇科技4 小时前
郑州无代码APP开发公司哪家好呢?推荐
开发语言
周末也要写八哥4 小时前
代码中的注释的重要性(二)
开发语言·python