C# OnnxRuntime 部署 DDColor

目录

说明

效果

模型信息

项目

代码

下载


说明

地址:https://github.com/piddnad/DDColor

效果

模型信息

Model Properties



Inputs


name:input

tensor:Float[1, 3, 256, 256]


Outputs


name:output

tensor:Float[1, 2, 256, 256]


项目

代码

using Microsoft.ML.OnnxRuntime;

using Microsoft.ML.OnnxRuntime.Tensors;

using OpenCvSharp;

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Imaging;

using System.Linq;

using System.Windows.Forms;

using Size = OpenCvSharp.Size;

namespace Onnx_Demo

{

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

}

private readonly string fileFilter = "图像文件|*.bmp;*.jpg;*.jpeg;*.tiff;*.png";

private string imagePath = "";

private string modelPath;

private Mat originalImage;

private Mat resultImage;

private SessionOptions sessionOptions;

private InferenceSession onnxSession;

private Tensor<float> inputTensor;

private List<NamedOnnxValue> inputContainer;

private IDisposableReadOnlyCollection<DisposableNamedOnnxValue> inferenceResult;

private DisposableNamedOnnxValue[] resultOnnxValues;

private Tensor<float> outputTensor;

private const int ModelInputSize = 256; // 模型固定输入尺寸

private void Form1_Load(object sender, EventArgs e)

{

modelPath = "model/ddcolor_paper_tiny.onnx";

sessionOptions = new SessionOptions();

sessionOptions.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;

sessionOptions.AppendExecutionProvider_CPU(0);

// 如需 GPU:sessionOptions.AppendExecutionProvider_CUDA(0);

onnxSession = new InferenceSession(modelPath, sessionOptions);

inputContainer = new List<NamedOnnxValue>();

string testImg = "test_img/Einstein, Rejection, and Crafting a Future.jpeg";

if (System.IO.File.Exists(testImg))

{

imagePath = testImg;

pictureBox1.Image = new Bitmap(imagePath);

originalImage = new Mat(imagePath);

}

}

private void button1_Click(object sender, EventArgs e)

{

OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = fileFilter;

if (ofd.ShowDialog() != DialogResult.OK) return;

imagePath = ofd.FileName;

pictureBox1.Image?.Dispose();

pictureBox1.Image = new Bitmap(imagePath);

originalImage = new Mat(imagePath);

pictureBox2.Image = null;

textBox1.Text = "";

}

private void button2_Click(object sender, EventArgs e)

{

if (string.IsNullOrEmpty(imagePath) || originalImage == null)

{

MessageBox.Show("请先选择图片!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);

return;

}

button2.Enabled = false;

pictureBox2.Image = null;

textBox1.Text = "";

Application.DoEvents();

try

{

int origH = originalImage.Height;

int origW = originalImage.Width;

// ========== 1. 预处理 ==========

// 1.1 BGR -> RGB,归一化到 [0,1] 浮点

Mat rgb = new Mat();

Cv2.CvtColor(originalImage, rgb, ColorConversionCodes.BGR2RGB);

rgb.ConvertTo(rgb, MatType.CV_32FC3, 1.0 / 255.0);

// 1.2 RGB -> LAB,提取 L 通道(范围 0~100)

Mat lab = new Mat();

Cv2.CvtColor(rgb, lab, ColorConversionCodes.RGB2Lab);

Mat[] labChannels = Cv2.Split(lab);

Mat L = labChannels[0]; // L 通道,float,范围 0~100

Mat originalL = L.Clone(); // 保存原始 L 用于最终合并

// 1.3 构造模型输入:灰度 RGB 图(通过 Lab -> RGB 转换,与 Python 代码一致)

// 调整 L 到模型输入尺寸 256x256

Mat resizedL = new Mat();

Cv2.Resize(L, resizedL, new Size(ModelInputSize, ModelInputSize), 0, 0, InterpolationFlags.Linear);

// 创建三通道 Lab 图像:L=resizedL, a=0, b=0

Mat grayLab = new Mat();

Mat[] grayLabChannels = new Mat[3];

grayLabChannels[0] = resizedL;

grayLabChannels[1] = Mat.Zeros(resizedL.Size(), MatType.CV_32FC1);

grayLabChannels[2] = Mat.Zeros(resizedL.Size(), MatType.CV_32FC1);

Cv2.Merge(grayLabChannels, grayLab);

// Lab -> RGB,得到灰度 RGB 图像(范围 0~1)

Mat grayRgb = new Mat();

Cv2.CvtColor(grayLab, grayRgb, ColorConversionCodes.Lab2RGB);

// 1.4 转换为 CHW 格式的 float 数组

int height = grayRgb.Height;

int width = grayRgb.Width;

float[] inputData = new float[3 * height * width];

Mat[] rgbChannels = Cv2.Split(grayRgb); // 顺序: R, G, B

int index = 0;

for (int c = 0; c < 3; c++)

{

float[] channelData = new float[height * width];

System.Runtime.InteropServices.Marshal.Copy(rgbChannels[c].Data, channelData, 0, height * width);

foreach (float val in channelData)

inputData[index++] = val;

}

var inputTensor = new DenseTensor<float>(inputData, new[] { 1, 3, height, width });

// ========== 2. 推理 ==========

var inputs = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input", inputTensor) };

DateTime t1 = DateTime.Now;

using (var results = onnxSession.Run(inputs))

{

DateTime t2 = DateTime.Now;

var output = results.First(item => item.Name == "output").AsTensor<float>();

float[] outputData = output.ToArray(); // shape: [1, 2, 256, 256]

float minVal = outputData.Min();

float maxVal = outputData.Max();

// ========== 3. 后处理 ==========

int outH = ModelInputSize;

int outW = ModelInputSize;

// 3.1 将输出 ab 通道转换为双通道 Mat (HWC, float)

Mat abMat = new Mat(outH, outW, MatType.CV_32FC2);

int idx = 0;

for (int y = 0; y < outH; y++)

{

for (int x = 0; x < outW; x++)

{

Vec2f ab;

ab.Item0 = outputData[idx]; // a 通道

ab.Item1 = outputData[idx + outH * outW]; // b 通道

abMat.Set(y, x, ab);

idx++;

}

}

// 3.2 将 ab 双通道图放大到原始图像尺寸(双线性插值)

Mat resizedAb = new Mat();

Cv2.Resize(abMat, resizedAb, new Size(origW, origH), 0, 0, InterpolationFlags.Linear);

// 3.3 合并原始 L 与放大后的 ab 得到 LAB 图像

Mat[] labResultChannels = new Mat[3];

labResultChannels[0] = originalL; // L (0~100)

Mat[] abChannels = Cv2.Split(resizedAb); // abChannels[0] = a, abChannels[1] = b

labResultChannels[1] = abChannels[0];

labResultChannels[2] = abChannels[1];

Mat labResult = new Mat();

Cv2.Merge(labResultChannels, labResult);

// 3.4 LAB -> RGB -> BGR(用于显示/保存)

Mat rgbResult = new Mat();

Cv2.CvtColor(labResult, rgbResult, ColorConversionCodes.Lab2RGB);

Mat bgrResult = new Mat();

Cv2.CvtColor(rgbResult, bgrResult, ColorConversionCodes.RGB2BGR);

// 3.5 将像素值从 [0,1] 转到 [0,255] 并转为 8UC3

bgrResult.ConvertTo(bgrResult, MatType.CV_8UC3, 255.0);

resultImage = bgrResult.Clone();

pictureBox2.Image = new Bitmap(resultImage.ToMemoryStream());

textBox1.Text = $"推理耗时: {(t2 - t1).TotalMilliseconds:F2} ms";

// 释放资源

abMat.Dispose();

resizedAb.Dispose();

foreach (var m in abChannels) m.Dispose();

labResult.Dispose();

rgbResult.Dispose();

}

// 释放资源

rgb.Dispose();

lab.Dispose();

L.Dispose();

resizedL.Dispose();

grayLab.Dispose();

grayRgb.Dispose();

foreach (var m in rgbChannels) m.Dispose();

foreach (var m in labChannels) m.Dispose();

foreach (var m in grayLabChannels) m.Dispose();

}

catch (Exception ex)

{

MessageBox.Show($"推理失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);

textBox1.Text = "推理出错";

}

finally

{

button2.Enabled = true;

}

}

private void button3_Click(object sender, EventArgs e)

{

if (resultImage == null || resultImage.Empty())

{

MessageBox.Show("请先进行推理!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

return;

}

SaveFileDialog sfd = new SaveFileDialog();

sfd.Title = "保存图像";

sfd.Filter = "PNG图片 (*.png)|*.png|JPEG图片 (*.jpg)|*.jpg|BMP图片 (*.bmp)|*.bmp";

sfd.FilterIndex = 1;

if (sfd.ShowDialog() == DialogResult.OK)

{

string ext = System.IO.Path.GetExtension(sfd.FileName).ToLower();

ImageFormat format = ImageFormat.Png;

if (ext == ".jpg" || ext == ".jpeg")

format = ImageFormat.Jpeg;

else if (ext == ".bmp")

format = ImageFormat.Bmp;

using (var stream = resultImage.ToMemoryStream())

using (var bitmap = new Bitmap(stream))

{

bitmap.Save(sfd.FileName, format);

}

MessageBox.Show($"保存成功!\n位置: {sfd.FileName}", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);

}

}

private void pictureBox1_DoubleClick(object sender, EventArgs e)

{

ShowImageInNormalWindow(pictureBox1.Image);

}

private void pictureBox2_DoubleClick(object sender, EventArgs e)

{

ShowImageInNormalWindow(pictureBox2.Image);

}

private void ShowImageInNormalWindow(Image img)

{

if (img == null) return;

Form frm = new Form

{

Width = img.Width + 20,

Height = img.Height + 40,

StartPosition = FormStartPosition.CenterScreen,

Text = "查看大图"

};

PictureBox pb = new PictureBox

{

Image = img,

Dock = DockStyle.Fill,

SizeMode = PictureBoxSizeMode.Zoom

};

frm.Controls.Add(pb);

frm.ShowDialog();

}

}

}

复制代码
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Windows.Forms;
using Size = OpenCvSharp.Size;

namespace Onnx_Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private readonly string fileFilter = "图像文件|*.bmp;*.jpg;*.jpeg;*.tiff;*.png";
        private string imagePath = "";
        private string modelPath;
        private Mat originalImage;       
        private Mat resultImage;            
        private SessionOptions sessionOptions;
        private InferenceSession onnxSession;
        private Tensor<float> inputTensor;
        private List<NamedOnnxValue> inputContainer;
        private IDisposableReadOnlyCollection<DisposableNamedOnnxValue> inferenceResult;
        private DisposableNamedOnnxValue[] resultOnnxValues;
        private Tensor<float> outputTensor;

        private const int ModelInputSize = 256;   // 模型固定输入尺寸

        private void Form1_Load(object sender, EventArgs e)
        {
            modelPath = "model/ddcolor_paper_tiny.onnx";
            sessionOptions = new SessionOptions();
            sessionOptions.LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_INFO;
            sessionOptions.AppendExecutionProvider_CPU(0);
            // 如需 GPU:sessionOptions.AppendExecutionProvider_CUDA(0);
            onnxSession = new InferenceSession(modelPath, sessionOptions);
            inputContainer = new List<NamedOnnxValue>();

            string testImg = "test_img/Einstein, Rejection, and Crafting a Future.jpeg";
            if (System.IO.File.Exists(testImg))
            {
                imagePath = testImg;
                pictureBox1.Image = new Bitmap(imagePath);
                originalImage = new Mat(imagePath);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            imagePath = ofd.FileName;
            pictureBox1.Image?.Dispose();
            pictureBox1.Image = new Bitmap(imagePath);
            originalImage = new Mat(imagePath);
            pictureBox2.Image = null;
            textBox1.Text = "";
        }


        private void button2_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(imagePath) || originalImage == null)
            {
                MessageBox.Show("请先选择图片!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            button2.Enabled = false;
            pictureBox2.Image = null;
            textBox1.Text = "";
            Application.DoEvents();

            try
            {
                int origH = originalImage.Height;
                int origW = originalImage.Width;

                // ========== 1. 预处理 ==========
                // 1.1 BGR -> RGB,归一化到 [0,1] 浮点
                Mat rgb = new Mat();
                Cv2.CvtColor(originalImage, rgb, ColorConversionCodes.BGR2RGB);
                rgb.ConvertTo(rgb, MatType.CV_32FC3, 1.0 / 255.0);

                // 1.2 RGB -> LAB,提取 L 通道(范围 0~100)
                Mat lab = new Mat();
                Cv2.CvtColor(rgb, lab, ColorConversionCodes.RGB2Lab);
                Mat[] labChannels = Cv2.Split(lab);
                Mat L = labChannels[0];   // L 通道,float,范围 0~100
                Mat originalL = L.Clone(); // 保存原始 L 用于最终合并

                // 1.3 构造模型输入:灰度 RGB 图(通过 Lab -> RGB 转换,与 Python 代码一致)
                // 调整 L 到模型输入尺寸 256x256
                Mat resizedL = new Mat();
                Cv2.Resize(L, resizedL, new Size(ModelInputSize, ModelInputSize), 0, 0, InterpolationFlags.Linear);

                // 创建三通道 Lab 图像:L=resizedL, a=0, b=0
                Mat grayLab = new Mat();
                Mat[] grayLabChannels = new Mat[3];
                grayLabChannels[0] = resizedL;
                grayLabChannels[1] = Mat.Zeros(resizedL.Size(), MatType.CV_32FC1);
                grayLabChannels[2] = Mat.Zeros(resizedL.Size(), MatType.CV_32FC1);
                Cv2.Merge(grayLabChannels, grayLab);

                // Lab -> RGB,得到灰度 RGB 图像(范围 0~1)
                Mat grayRgb = new Mat();
                Cv2.CvtColor(grayLab, grayRgb, ColorConversionCodes.Lab2RGB);

                // 1.4 转换为 CHW 格式的 float 数组
                int height = grayRgb.Height;
                int width = grayRgb.Width;
                float[] inputData = new float[3 * height * width];
                Mat[] rgbChannels = Cv2.Split(grayRgb); // 顺序: R, G, B
                int index = 0;
                for (int c = 0; c < 3; c++)
                {
                    float[] channelData = new float[height * width];
                    System.Runtime.InteropServices.Marshal.Copy(rgbChannels[c].Data, channelData, 0, height * width);
                    foreach (float val in channelData)
                        inputData[index++] = val;
                }

                var inputTensor = new DenseTensor<float>(inputData, new[] { 1, 3, height, width });

                // ========== 2. 推理 ==========
                var inputs = new List<NamedOnnxValue> { NamedOnnxValue.CreateFromTensor("input", inputTensor) };
                DateTime t1 = DateTime.Now;
                using (var results = onnxSession.Run(inputs))
                {
                    DateTime t2 = DateTime.Now;
                    var output = results.First(item => item.Name == "output").AsTensor<float>();
                    float[] outputData = output.ToArray(); // shape: [1, 2, 256, 256]
                    float minVal = outputData.Min();
                    float maxVal = outputData.Max();

                    // ========== 3. 后处理 ==========
                    int outH = ModelInputSize;
                    int outW = ModelInputSize;

                    // 3.1 将输出 ab 通道转换为双通道 Mat (HWC, float)
                    Mat abMat = new Mat(outH, outW, MatType.CV_32FC2);
                    int idx = 0;
                    for (int y = 0; y < outH; y++)
                    {
                        for (int x = 0; x < outW; x++)
                        {
                            Vec2f ab;
                            ab.Item0 = outputData[idx];                     // a 通道
                            ab.Item1 = outputData[idx + outH * outW];       // b 通道
                            abMat.Set(y, x, ab);
                            idx++;
                        }
                    }

                    // 3.2 将 ab 双通道图放大到原始图像尺寸(双线性插值)
                    Mat resizedAb = new Mat();
                    Cv2.Resize(abMat, resizedAb, new Size(origW, origH), 0, 0, InterpolationFlags.Linear);

                    // 3.3 合并原始 L 与放大后的 ab 得到 LAB 图像
                    Mat[] labResultChannels = new Mat[3];
                    labResultChannels[0] = originalL;                       // L (0~100)
                    Mat[] abChannels = Cv2.Split(resizedAb);                // abChannels[0] = a, abChannels[1] = b
                    labResultChannels[1] = abChannels[0];
                    labResultChannels[2] = abChannels[1];

                    Mat labResult = new Mat();
                    Cv2.Merge(labResultChannels, labResult);

                    // 3.4 LAB -> RGB -> BGR(用于显示/保存)
                    Mat rgbResult = new Mat();
                    Cv2.CvtColor(labResult, rgbResult, ColorConversionCodes.Lab2RGB);
                    Mat bgrResult = new Mat();
                    Cv2.CvtColor(rgbResult, bgrResult, ColorConversionCodes.RGB2BGR);

                    // 3.5 将像素值从 [0,1] 转到 [0,255] 并转为 8UC3
                    bgrResult.ConvertTo(bgrResult, MatType.CV_8UC3, 255.0);
                    resultImage = bgrResult.Clone();

                    pictureBox2.Image = new Bitmap(resultImage.ToMemoryStream());
                    textBox1.Text = $"推理耗时: {(t2 - t1).TotalMilliseconds:F2} ms";

                    // 释放资源
                    abMat.Dispose();
                    resizedAb.Dispose();
                    foreach (var m in abChannels) m.Dispose();
                    labResult.Dispose();
                    rgbResult.Dispose();

                }

                // 释放资源
                rgb.Dispose();
                lab.Dispose();
                L.Dispose();
                resizedL.Dispose();
                grayLab.Dispose();
                grayRgb.Dispose();
                foreach (var m in rgbChannels) m.Dispose();
                foreach (var m in labChannels) m.Dispose();
                foreach (var m in grayLabChannels) m.Dispose();
               
            }
            catch (Exception ex)
            {
                MessageBox.Show($"推理失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                textBox1.Text = "推理出错";
            }
            finally
            {
                button2.Enabled = true;
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (resultImage == null || resultImage.Empty())
            {
                MessageBox.Show("请先进行推理!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Title = "保存图像";
            sfd.Filter = "PNG图片 (*.png)|*.png|JPEG图片 (*.jpg)|*.jpg|BMP图片 (*.bmp)|*.bmp";
            sfd.FilterIndex = 1;
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                string ext = System.IO.Path.GetExtension(sfd.FileName).ToLower();
                ImageFormat format = ImageFormat.Png;
                if (ext == ".jpg" || ext == ".jpeg")
                    format = ImageFormat.Jpeg;
                else if (ext == ".bmp")
                    format = ImageFormat.Bmp;

                using (var stream = resultImage.ToMemoryStream())
                using (var bitmap = new Bitmap(stream))
                {
                    bitmap.Save(sfd.FileName, format);
                }
                MessageBox.Show($"保存成功!\n位置: {sfd.FileName}", "完成", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            ShowImageInNormalWindow(pictureBox1.Image);
        }

        private void pictureBox2_DoubleClick(object sender, EventArgs e)
        {
            ShowImageInNormalWindow(pictureBox2.Image);
        }

        private void ShowImageInNormalWindow(Image img)
        {
            if (img == null) return;
            Form frm = new Form
            {
                Width = img.Width + 20,
                Height = img.Height + 40,
                StartPosition = FormStartPosition.CenterScreen,
                Text = "查看大图"
            };
            PictureBox pb = new PictureBox
            {
                Image = img,
                Dock = DockStyle.Fill,
                SizeMode = PictureBoxSizeMode.Zoom
            };
            frm.Controls.Add(pb);
            frm.ShowDialog();
        }
    }
}

下载

源码下载

相关推荐
HyperAI超神经1 天前
教程汇总丨开源小模型综合智能水平追平GPT-5,一站测评Qwen 3.5/Gemma 4等热门模型
人工智能
观远数据1 天前
跨部门指标统一治理:如何消除数据口径歧义提升决策效率
大数据·人工智能·数据挖掘·数据分析
常宇杏起1 天前
AI安全进阶:AI模型鲁棒性测试的核心方法
大数据·人工智能·安全
搜佛说1 天前
sfsEdgeStore,工业物联网边缘计算的“瘦身”革命
人工智能·物联网·边缘计算
算力百科小智1 天前
2026 年深度学习 GPU 算力租用平台全面对比
人工智能·智星云·gpu算力租用
木泽八1 天前
2026年网络安全威胁全景:AI攻防新纪元完全指南
人工智能·安全·web安全
MediaTea1 天前
知识图谱 04:知识表示模型
人工智能·知识图谱
词元Max1 天前
Java 转 AI Agent 开发学习路线(2026年3月最新版)
java·人工智能·学习
实在智能RPA1 天前
Agent如何帮助企业减少人为操作失误?——2026年企业级智能体闭环执行与风险治理深度拆解
人工智能·ai
数字卢语1 天前
如何从 0 搭建 Hermes Agent,并打通微信的(完整踩坑与排错记录)
人工智能