본문 바로가기
[ Program ]/YOLO11 c#

7.c# 에서 탐지

by 관이119 2025. 1. 22.

위와 같이 새프로젝트를 만들고 픽처박스 하나와 버튼을 하나 추가한다.

 

 

 

 

Nuget 패키지 관리에 들어간다.

 

 

찾아보기에서 onnxruntime 으로 검색하면 위와 같이 나오는데 그중 onnxruntime 과 onnxruntime.extensions 를 설치한다.

 

 

System.Drawing.common 도 검색해서 설치한다.

 

 

 

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;

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

        private void btnLoadImage_Click(object sender, EventArgs e)
        {
            // 이미지 선택 다이얼로그
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "Image Files|*.jpg;*.jpeg;*.png;*.bmp",
                Title = "Select an Image"
            };

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                string imagePath = openFileDialog.FileName;

                // ONNX 모델 실행
                Bitmap resultImage = RunOnnxModel(imagePath);

                if (resultImage != null)
                {
                    // 결과 이미지를 PictureBox에 표시
                    pictureBox1.Image = resultImage;
                }
                else
                {
                    MessageBox.Show("Failed to process the image!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private Bitmap RunOnnxModel(string imagePath)
        {
            try
            {
                // ONNX 모델 경로
                string modelPath = @"C:\Users\L\runs\detect\train\weights\best.onnx";

                using (var session = new InferenceSession(modelPath))
                {
                    // 1. 이미지 전처리
                    using (var inputImage = LoadBitmapWithoutLock(imagePath)) // 파일 잠금 해제된 방식으로 로드
                    {
                        int inputSize = 640;
                        using (var resizedImage = new Bitmap(inputImage, new Size(inputSize, inputSize)))
                        {
                            var inputTensor = PreprocessImage(resizedImage);

                            // 2. 모델 추론
                            var inputs = new List<NamedOnnxValue>
                            {
                                NamedOnnxValue.CreateFromTensor("images", inputTensor)
                            };

                            using (var results = session.Run(inputs))
                            {
                                var output = results.First().AsEnumerable<float>().ToArray();
                                var detections = PostProcessOutput(output, inputSize, inputImage.Width, inputImage.Height);

                                // 3. 결과 시각화
                                using (var graphics = Graphics.FromImage(inputImage))
                                {
                                    foreach (var detection in detections)
                                    {
                                        var rect = new Rectangle(
                                            (int)detection.BoundingBox.X,
                                            (int)detection.BoundingBox.Y,
                                            (int)detection.BoundingBox.Width,
                                            (int)detection.BoundingBox.Height
                                        );
                                        using (var pen = new Pen(Color.Green, 2))
                                        {
                                            graphics.DrawRectangle(pen, rect);
                                        }

                                        using (var font = new Font("Arial", 12))
                                        using (var brush = new SolidBrush(Color.Green))
                                        {
                                            graphics.DrawString(
                                                $"Conf: {detection.Confidence:F2}",
                                                font,
                                                brush,
                                                new PointF(rect.X, rect.Y)
                                            );
                                        }
                                    }
                                }

                                // 결과 이미지를 반환 (메모리 내)
                                return new Bitmap(inputImage);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }
        }


        private Bitmap LoadBitmapWithoutLock(string imagePath)
        {
            using (var stream = new MemoryStream(File.ReadAllBytes(imagePath)))
            {
                return new Bitmap(stream);
            }
        }


        private Tensor<float> PreprocessImage(Bitmap image)
        {
            int inputSize = 640;
            var tensor = new DenseTensor<float>(new[] { 1, 3, inputSize, inputSize });

            for (int y = 0; y < inputSize; y++)
            {
                for (int x = 0; x < inputSize; x++)
                {
                    var pixel = image.GetPixel(x, y);

                    // RGB 값 정규화 (0~1 범위로)
                    tensor[0, 0, y, x] = pixel.R / 255.0f;
                    tensor[0, 1, y, x] = pixel.G / 255.0f;
                    tensor[0, 2, y, x] = pixel.B / 255.0f;
                }
            }

            return tensor;
        }

#if false //겹치는 박스 표시
        private List<DetectionResult> PostProcessOutput(float[] output, int inputSize, int origWidth, int origHeight)
        {
            // 출력이 (5, 8400) 형태
            int numBoxes = output.Length / 5;
            int stride = numBoxes; // stride는 각 채널당 박스 개수

            var results = new List<DetectionResult>();

            for (int i = 0; i < stride; i++) // stride = 8400
            {
                // 각 채널에서 해당 박스 정보 추출
                float cx = output[i];             // x 좌표 (중심)
                float cy = output[i + stride];    // y 좌표 (중심)
                float w = output[i + 2 * stride]; // 너비
                float h = output[i + 3 * stride]; // 높이
                float confidence = output[i + 4 * stride]; // 신뢰도

                Console.WriteLine($"cx: {cx}, cy: {cy}, w: {w}, h: {h}, confidence: {confidence}");

                // 신뢰도 필터링
                if (confidence > 0.8f) // 신뢰도 80% 이상만 처리
                {
                    // 좌표 변환 (중심 좌표 -> 좌상단-우하단 좌표)
                    float x1 = (cx - w / 2) * origWidth / inputSize;
                    float y1 = (cy - h / 2) * origHeight / inputSize;
                    float x2 = (cx + w / 2) * origWidth / inputSize;
                    float y2 = (cy + h / 2) * origHeight / inputSize;

                    results.Add(new DetectionResult
                    {
                        BoundingBox = new RectangleF(x1, y1, x2 - x1, y2 - y1),
                        Confidence = confidence
                    });
                }
            }

            return results;
        }
#else //겹치는 박스 제거
        private List<DetectionResult> PostProcessOutput(float[] output, int inputSize, int origWidth, int origHeight)
        {
            int numBoxes = output.Length / 5;
            int stride = numBoxes;

            var detections = new List<DetectionResult>();

            for (int i = 0; i < stride; i++)
            {
                float cx = output[i];             // x 좌표 (중심)
                float cy = output[i + stride];    // y 좌표 (중심)
                float w = output[i + 2 * stride]; // 너비
                float h = output[i + 3 * stride]; // 높이
                float confidence = output[i + 4 * stride]; // 신뢰도

                Console.WriteLine($"cx: {cx}, cy: {cy}, w: {w}, h: {h}, confidence: {confidence}");

                if (confidence > 0.8f) // 신뢰도 80% 이상만 처리
                {
                    float x1 = (cx - w / 2) * origWidth / inputSize;
                    float y1 = (cy - h / 2) * origHeight / inputSize;
                    float x2 = (cx + w / 2) * origWidth / inputSize;
                    float y2 = (cy + h / 2) * origHeight / inputSize;

                    detections.Add(new DetectionResult
                    {
                        BoundingBox = new RectangleF(x1, y1, x2 - x1, y2 - y1),
                        Confidence = confidence
                    });
                }
            }

            // NMS 적용
            return ApplyNMS(detections, 0.5f); // IoU 임계값 0.5
        }

        private List<DetectionResult> ApplyNMS(List<DetectionResult> detections, float iouThreshold)
        {
            var results = new List<DetectionResult>();

            // 신뢰도를 기준으로 내림차순 정렬
            var sortedDetections = detections.OrderByDescending(d => d.Confidence).ToList();

            while (sortedDetections.Any())
            {
                // 신뢰도가 가장 높은 박스를 선택
                var current = sortedDetections.First();
                results.Add(current);
                sortedDetections.RemoveAt(0);

                // 나머지 박스와 IoU(Intersection over Union) 계산
                sortedDetections = sortedDetections.Where(other =>
                {
                    float iou = CalculateIoU(current.BoundingBox, other.BoundingBox);
                    return iou < iouThreshold; // iouThreshold 이상 겹치면 제거
                }).ToList();
            }

            return results;
        }

        private float CalculateIoU(RectangleF box1, RectangleF box2)
        {
            float x1 = Math.Max(box1.Left, box2.Left);
            float y1 = Math.Max(box1.Top, box2.Top);
            float x2 = Math.Min(box1.Right, box2.Right);
            float y2 = Math.Min(box1.Bottom, box2.Bottom);

            float intersection = Math.Max(0, x2 - x1) * Math.Max(0, y2 - y1);
            float area1 = box1.Width * box1.Height;
            float area2 = box2.Width * box2.Height;

            float union = area1 + area2 - intersection;
            return intersection / union;
        }
#endif
    }
    public class DetectionResult
    {
        public RectangleF BoundingBox { get; set; }
        public float Confidence { get; set; }
    }
}

코드는 위와 같이 만들어주자.

 

 

 

실행하면 위와 같이 잘잡는걸 볼수 있다.

왼쪽 두마리가 하나로 잡히긴했는데 귀여우니까 그냥 넘어가자.

학습량이 늘어나면 개별로 잡을수도 있다.

 

'[ Program ] > YOLO11 c#' 카테고리의 다른 글

6.ONNX 모델 검증  (0) 2025.01.22
5. ONNX 파일로 변환  (0) 2025.01.22
4. labelimg 설치및 사용법  (0) 2025.01.22
3.YOLO11 모델로 학습  (0) 2025.01.22
2. ultralytics 설치  (0) 2025.01.22

댓글