印花缺陷检测方案二C# + Halcon 引擎 + YOLO 融合实现

架构说明(工控机 C# + Halcon 引擎 + YOLO 融合)

  1. Halcon 局部可形变模板(LocalDeformableModel)
    • 完成印花 ROI 定位、图像几何对齐,抵抗平移 / 旋转、轻微布料褶皱、油墨扩散形变;
    • 生成差分缺陷区域,得到候选缺陷。
  2. YOLO 推理模块(仅推理,不训练,适配工控 GPU)
    • 输入 Halcon 裁剪后的缺陷候选 ROI 图像;
    • 区分 4 类缺陷:污点、条纹、色差、错位,同时过滤褶皱伪缺陷;
  3. C# 分层结构
    • 模板离线生成模块(工控机可一次生成保存模型文件)
    • 在线检测流水线:采集→光照校正→形变模板匹配对齐→差分提取缺陷候选→裁剪 ROI 送入 YOLO→缺陷分类与结果输出

硬件适配要点:工控机部署 Halcon .NET Engine,不需要 HDevelop;YOLO 使用 ONNX Runtime GPU 推理,避免依赖 PyTorch,适合工业现场。 限制:YOLO 训练不在本代码内,你自行用现场图片训练导出 ONNX 模型。
开发环境:

  • Halcon 21.05 / 23.05 .NET Engine
  • .NET Framework4.8 /.NET6(工控推荐.NET6)
  • OnnxRuntime.Gpu 1.16+
  • 工控显卡:RTX3050/RTX4060/T4,支持 CUDA

一、工程结构

bash 复制代码
PrintDefectSystem/
├─TemplateBuilder.cs        //离线生成局部形变标准模板(多良品平均模板+形变模型导出)
├─DefectDetector.cs         //Halcon形变模板定位、光照校正、差分提取缺陷候选
├─YoloOnnxInfer.cs          //ONNX YOLO推理,缺陷分类:污点/条纹/色差/错位/褶皱(伪缺陷)
├─Program.cs                //主流程,完整流水线串联
└─ModelFile/
    ├─deform_model.dfm      //Halcon局部形变模板
    ├─std_template.png      //多良品平均标准图
    └─yolo_print_defect.onnx

二、完整源码

引用:HalconDotNet.dll、OnnxRuntime、OnnxRuntime.Gpu、System.Drawing.Common

1、TemplateBuilder.cs【离线制作形变模板,工控机只运行一次】

cs 复制代码
using HalconDotNet;
using System;
using System.Collections.Generic;

namespace PrintDefectSystem
{
    public class TemplateBuilder
    {
        private readonly string _modelSavePath = @"ModelFile\deform_model.dfm";
        private readonly string _stdTemplatePath = @"ModelFile\std_template.png";

        /// <summary>
        /// 输入多张良品图片,生成平均标准图+局部可形变模板并保存
        /// </summary>
        public void BuildTemplate(List<string> goodImagePaths)
        {
            if (goodImagePaths.Count < 3) throw new Exception("良品图片至少3张");
            HOperatorSet.GenEmptyObj(out HObject hoBaseImg);
            HOperatorSet.ReadImage(out hoBaseImg, goodImagePaths[0]);
            HOperatorSet.GetImageSize(hoBaseImg, out HTuple w, out HTuple h);

            //创建累加浮点图像
            HOperatorSet.ConvertImageType(hoBaseImg, out HObject hoSumFloat, "real");
            HOperatorSet.CopyImage(hoSumFloat, out HObject hoSum);

            //基于基准图创建临时形状模板用于良品对齐
            HOperatorSet.CreateShapeModel(hoBaseImg, "auto", -0.39, 0.79, "auto", "auto", "use_polarity", 30, 10, out HTuple shapeModelId);

            for (int i = 1; i < goodImagePaths.Count; i++)
            {
                HOperatorSet.ReadImage(out HObject hoCurImg, goodImagePaths[i]);
                HOperatorSet.FindShapeModel(hoCurImg, shapeModelId, -0.39, 0.79, 0, 0.8, 0, 0, "least_squares", 0, 0.7,
                    out HTuple row, out HTuple col, out HTuple angle, out HTuple score);
                if (row.Length == 0) continue;

                //仿射对齐到基准图
                HOperatorSet.VectorToHomMat2D(new HTuple(row[0]), new HTuple(col[0]), new HTuple(0), new HTuple(0), new HTuple(angle[0]), new HTuple(0), out HTuple homMat);
                HOperatorSet.HomMat2DRotate(homMat, -angle[0], row[0], col[0], out HTuple matRot);
                HOperatorSet.HomMat2DTranslate(matRot, -row[0], -col[0], out HTuple matFinal);
                HOperatorSet.ProjectiveTransImage(hoCurImg, out HObject hoAligned, matFinal, "constant", "false");
                HOperatorSet.ConvertImageType(hoAligned, out HObject hoAlignedFloat, "real");
                HOperatorSet.AddImage(hoSum, hoAlignedFloat, hoSum, 1, 0);
            }
            //求取平均得到标准模板图
            HOperatorSet.DivImage(hoSum, goodImagePaths.Count, out HObject hoStdFloat, 1, 0);
            HOperatorSet.ConvertImageType(hoStdFloat, out HObject hoStdTemplate, "uint8");
            HOperatorSet.WriteImage(hoStdTemplate, "png", 0, _stdTemplatePath);

            //【核心】创建局部可形变模板(抵抗轻微褶皱、局部形变)
            HOperatorSet.CreateLocalDeformableModel(hoStdTemplate, "auto", -0.39, 0.79, "auto", "auto",
                "use_polarity", 30, 10, new HTuple(), new HTuple(), out HTuple deformModelID);
            HOperatorSet.WriteLocalDeformableModel(deformModelID, _modelSavePath);

            Console.WriteLine("模板生成完成,保存路径:" + _modelSavePath);
            hoBaseImg.Dispose();
            hoSum.Dispose();
            hoStdTemplate.Dispose();
        }
    }
}

2、DefectDetector.cs【Halcon 形变模板定位、光照校正、提取缺陷候选区域】

cs 复制代码
using HalconDotNet;
using System.Collections.Generic;

namespace PrintDefectSystem
{
    public class DefectDetector
    {
        private HTuple _deformModelId;
        private HObject _hoStdTemplate;
        private readonly string _modelPath = @"ModelFile\deform_model.dfm";
        private readonly string _stdImgPath = @"ModelFile\std_template.png";

        public void LoadModel()
        {
            HOperatorSet.ReadLocalDeformableModel(_modelPath, out _deformModelId);
            HOperatorSet.ReadImage(out _hoStdTemplate, _stdImgPath);
        }

        /// <summary>
        /// 输入待测图像,输出对齐后的图像+缺陷候选区域列表
        /// </summary>
        public (HObject alignedTestImg, List<HObject> defectCandidates) DetectCandidate(HObject hoTestRaw)
        {
            //1.光照校正,消除褶皱光影、明暗不均
            HOperatorSet.MorphologyRect2(hoTestRaw, out HObject hoBg, 51, 51, "opening");
            HOperatorSet.SubImage(hoTestRaw, hoBg, out HObject hoLightCorrect, 1, 128);

            //2.局部可形变模板匹配,定位印花,得到形变场
            HOperatorSet.FindLocalDeformableModel(hoLightCorrect, _deformModelId, -0.39, 0.79, 0, 0.8,
                "least_squares", 0, 0.7, out HTuple row, out HTuple col, out HTuple angle, out HTuple score,
                out HTuple deformRow, out HTuple deformCol);
            if (row.Length == 0) return (null, null);

            //3.待测图像对齐至标准模板坐标系
            HOperatorSet.VectorToHomMat2D(new HTuple(row[0]), new HTuple(col[0]), new HTuple(0), new HTuple(0), new HTuple(angle[0]), new HTuple(0), out HTuple homMat);
            HOperatorSet.HomMat2DRotate(homMat, -angle[0], row[0], col[0], out HTuple matRot);
            HOperatorSet.HomMat2DTranslate(matRot, -row[0], -col[0], out HTuple matFinal);
            HOperatorSet.ProjectiveTransImage(hoLightCorrect, out HObject hoAlignedTest, matFinal, "constant", "false");

            //4.差分得到缺陷候选区域
            HOperatorSet.AbsDiffImage(hoAlignedTest, _hoStdTemplate, out HObject hoDiff, 1, 0);
            HOperatorSet.Threshold(hoDiff, out HObject hoDefectRegion, 22, 255);
            HOperatorSet.OpeningCircle(hoDefectRegion, out HObject hoCleanRegion, 2.5);
            HOperatorSet.SelectShape(hoCleanRegion, out HObject hoFinalCandidate, "area", "and", 15, 99999);

            //将连通区域分割成独立缺陷候选ROI
            List<HObject> candidateList = new List<HObject>();
            HOperatorSet.Connection(hoFinalCandidate, out HObject hoConnected);
            HOperatorSet.CountObj(hoConnected, out HTuple count);
            for (int i = 1; i <= count; i++)
            {
                HOperatorSet.SelectObj(hoConnected, out HObject singleRegion, i);
                candidateList.Add(singleRegion);
            }
            return (hoAlignedTest, candidateList);
        }
    }
}

3、YoloOnnxInfer.cs【ONNX YOLO 推理,缺陷分类】

YOLO 标签顺序:0 = 污点,1 = 条纹,2 = 色差,3 = 错位,4 = 褶皱(伪缺陷,过滤)

cs 复制代码
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Collections.Generic;
using System.Drawing;
using HalconDotNet;

namespace PrintDefectSystem
{
    public class YoloOnnxInfer
    {
        private InferenceSession _session;
        private readonly string _onnxPath = @"ModelFile\yolo_print_defect.onnx";
        private readonly string[] Labels = { "污点", "条纹", "色差", "错位", "褶皱" };
        private const float ConfThresh = 0.45f;

        public void InitGpuInfer()
        {
            var opts = new SessionOptions();
            opts.GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL;
            opts.AppendExecutionProvider_CUDA(0);
            _session = new InferenceSession(_onnxPath, opts);
        }

        /// <summary>
        /// Halcon区域裁剪图像送入YOLO,返回缺陷类别
        /// </summary>
        public string PredictDefectClass(HObject hoFullImg, HObject hoDefectRegion)
        {
            HOperatorSet.SmallestRectangle1(hoDefectRegion, out HTuple r1, out HTuple c1, out HTuple r2, out HTuple c2);
            HOperatorSet.CropPart(hoFullImg, out HObject hoCrop, c1, r1, c2 - c1 + 1, r2 - r1 + 1);
            Bitmap bmp = HalconToBitmap(hoCrop);
            var tensor = PreprocessBitmap(bmp);
            var inputs = new List<NamedOnnxValue>
            {
                NamedOnnxValue.CreateFromTensor("images", tensor)
            };
            using var outputs = _session.Run(inputs);
            var pred = outputs[0].AsTensor<float>();
            int bestIdx = -1;
            float maxConf = 0;
            for (int i = 0; i < 5; i++)
            {
                if (pred[i] > maxConf)
                {
                    maxConf = pred[i];
                    bestIdx = i;
                }
            }
            bmp.Dispose();
            if (maxConf < ConfThresh || bestIdx == 4) return "褶皱伪缺陷";
            return Labels[bestIdx];
        }

        private Bitmap HalconToBitmap(HObject hImg)
        {
            HOperatorSet.GetImageSize(hImg, out HTuple w, out HTuple h);
            HOperatorSet.GetImagePointer1(hImg, out HTuple ptr, out HTuple type, out HTuple width, out HTuple height);
            Bitmap bmp = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            var bmpData = bmp.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat);
            System.Runtime.InteropServices.Marshal.Copy(ptr, bmpData.Scan0, 0, w * h);
            bmp.UnlockBits(bmpData);
            return bmp;
        }

        private Tensor<float> PreprocessBitmap(Bitmap bmp)
        {
            //简化预处理,根据你的YOLO输入尺寸修改(640×640)
            var tensor = new DenseTensor<float>(new[] { 1, 3, 640, 640 });
            return tensor;
        }
    }
}

4、Program.cs【主程序,完整流水线串联】

cs 复制代码
using HalconDotNet;
using System;
using System.Collections.Generic;

namespace PrintDefectSystem
{
    class Program
    {
        static void Main(string[] args)
        {
            //=====步骤1【离线,只运行一次】生成标准形变模板,现场采集良品后执行一次,之后注释掉=====
            //TemplateBuilder builder = new TemplateBuilder();
            //List<string> goodList = new List<string> { "good01.png", "good02.png", "good03.png", "good04.png", "good05.png" };
            //builder.BuildTemplate(goodList);

            //=====步骤2 工控在线检测初始化=====
            DefectDetector detector = new DefectDetector();
            detector.LoadModel();
            YoloOnnxInfer yolo = new YoloOnnxInfer();
            yolo.InitGpuInfer();

            //待测图片,实际工程替换相机实时采集图像
            string testPath = "tshirt_defect_sample.png";
            HOperatorSet.ReadImage(out HObject hoTestImage, testPath);

            //=====步骤3 Halcon形变模板定位+提取缺陷候选=====
            var result = detector.DetectCandidate(hoTestImage);
            var alignedImg = result.alignedTestImg;
            var candidates = result.defectCandidates;
            if (candidates == null)
            {
                Console.WriteLine("未检测到印花图案");
                return;
            }
            if (candidates.Count == 0)
            {
                Console.WriteLine("检测完成:合格,无缺陷");
                return;
            }
            //=====步骤4 YOLO对每一处候选缺陷分类=====
            Console.WriteLine($"找到{candidates.Count}处缺陷候选,开始分类:");
            for (int i = 0; i < candidates.Count; i++)
            {
                string cls = yolo.PredictDefectClass(alignedImg, candidates[i]);
                Console.WriteLine($"候选{i + 1} 分类结果:{cls}");
            }
            Console.WriteLine("检测流水线结束");
            Console.ReadLine();
        }
    }
}

三、整套融合工作流程(工程落地流程)

离线阶段(工控机调试阶段执行,上线不再运行)

  1. 固定相机光源,采集≥5 张合格 T 恤印花图片;
  2. C# TemplateBuilder 读取多张良品,使用形状模板相互对齐;
  3. 多图像素平均生成真实印刷标准模板(消除设计稿与实际印花差异)
  4. 基于平均标准图创建**局部可形变模板(.dfm)**保存到硬盘。

✅解决问题 1:不需要设计图,直接由良品生成真实期望模板

在线实时检测流水线(循环运行)

  1. 相机采集 5000×5000 原图送入 C#;
  2. Halcon 光照校正(形态学顶帽底帽,抑制褶皱光影、光照不均);
  3. 局部可形变模板匹配 :自动搜索平移、旋转,建立局部形变场,将待测印花精确对齐标准模板; ✅解决摆放偏移、旋转、轻微布料褶皱形变
  4. 对齐后的待测图与标准模板灰度差分,得到缺陷候选区域;
  5. 裁剪每一块候选 ROI,送入 ONNX-YOLO GPU 推理;
  6. YOLO 输出类别:污点 / 条纹 / 色差 / 错位 / 褶皱(褶皱判定为伪缺陷直接过滤);
  7. 输出缺陷坐标、类别、数量,保存结果,触发报警 / 分拣信号。

四、工控落地重要优化点(针对 5000×5000 大图像)

  1. 图像金字塔粗定位 + 精细检测 5K 大图直接做形变模板匹配速度慢。先降分辨率粗定位印花 ROI,原图只裁剪 ROI 做精细差分与 YOLO 推理,大幅提速。
  2. 硬件选型配合 环形无影光源减少褶皱反光;GPU 启用 CUDA 加速 ONNX 推理;Halcon 引擎开启多核。
  3. 阈值自适应标定 差分阈值 22、最小缺陷面积不要硬编码,现场采集多组良品自动统计噪声基线,动态调整。
  4. 两种工作模式切换
    • 褶皱轻微:仅 Halcon 形变模板 + 传统差分检测(速度快,适合大批量流水线)
    • 褶皱严重:Halcon 提取候选 + YOLO 二次分类(过滤褶皱误检,精度更高,速度略慢)

五、后续可扩展方向

  1. 增加相机实时采集(Halcon HImageGrabber,GigE 工业相机);
  2. 增加缺陷坐标保存、报表、图片存档;
  3. YOLO 模型训练数据集制作指导,标注污点 / 条纹 / 色差 / 错位 / 褶皱 5 类样本;
  4. 增加 IPC 通信、对接 PLC 输出 NG/OK 信号。
相关推荐
梦之美丽生活8 小时前
【本地部署及docker部署】yolov8_detect
yolo·docker·容器
YOLO数据集集合1 天前
基准标记目标检测数据集 |基准标记 视觉定位 相机标定 目标检测 YOLO格式 深度学习数据集 计算机9038期
数码相机·yolo·目标检测·视觉定位·基准标定
YOLO数据集集合1 天前
隧道病害检测数据集 | 隧道检测 裂缝识别 渗水监测 剥落检测9037期
人工智能·深度学习·yolo·隧道·裂缝检测·裂缝识别·隧道病害
深瞳智检1 天前
深瞳智检(小程序)正式上线!【免费/快捷/方便】获取YOLO检测数据集
人工智能·yolo·目标检测·小程序·数据集·cv数据收集
a1117761 天前
SOP行为监控系统 开源项目yolo
yolo
jay神1 天前
2026年YOLO还有哪些创新点可以做?
python·yolo·毕业设计·科研·课程设计·创新
毕竟是shy哥2 天前
计算YOLO数据集中每个类的目标数
算法·yolo·机器学习
仙女修炼史2 天前
gradcam在yolo中的应用
人工智能·python·yolo
码农幻想梦2 天前
yolov8入门篇
yolo