위와 같이 새프로젝트를 만들고 픽처박스 하나와 버튼을 하나 추가한다.
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 |
댓글