본문 바로가기

자료

C#에서 YOLO v3 연동

728x90
 static void Main()
        {
            #region parameter
            var image = "";
            var cfg = "";
            var model = "";
            const float threshold = 0.5f;       //신뢰 값 
            const float nmsThreshold = 0.3f;    //nms 임계 값
            #endregion

            //이미지를 얻는다
            var org = new Mat(image);

            // 블롭 설정, 크기 : 320/416/608
            // opencv blob 설정은 여기에서 확인할 수 있습니다 https://github.com/opencv/opencv/tree/master/samples/dnn#object-detection
            var blob = CvDnn.BlobFromImage(org, 1.0 / 255, new Size(416, 416), new Scalar(), true, false);

            // "separator_index <line.size ()"오류가 발생하면 모델 및 구성을로드하십시오. cfg 파일을 확인하십시오.
            var net = CvDnn.ReadNetFromDarknet(cfg, model);
            #region set preferable
            net.SetPreferableBackend(3);
            /*
            0:DNN_BACKEND_DEFAULT 
            1:DNN_BACKEND_HALIDE 
            2:DNN_BACKEND_INFERENCE_ENGINE
            3:DNN_BACKEND_OPENCV 
             */
            net.SetPreferableTarget(0);
            /*
            0:DNN_TARGET_CPU 
            1:DNN_TARGET_OPENCL
            2:DNN_TARGET_OPENCL_FP16
            3:DNN_TARGET_MYRIAD 
            4:DNN_TARGET_FPGA 
             */
            #endregion

             // 입력 데이터
            net.SetInput(blob);

            // 출력 레이어 이름을 얻습니다
            var outNames = net.GetUnconnectedOutLayersNames();
            // 출력 레이어를위한 매트 생성
            var outs = outNames.Select(_ => new Mat()).ToArray();

            #region forward model
            Stopwatch sw = new Stopwatch();
            sw.Start();

            net.Forward(outs, outNames);

            sw.Stop();
            Console.WriteLine($"Runtime:{sw.ElapsedMilliseconds} ms");
            #endregion

            // 모든 출력에서 결과를 얻습니다.
            GetResult(outs, org, threshold, nmsThreshold);

            using (new Window("died.tw", org))
            {
                Cv2.WaitKey();
            }
        }

private static void GetResult(IEnumerable<Mat> output, Mat image, float threshold,float nmsThreshold, bool nms=true)
        {
            // nms의 경우
            var classIds = new List<int>();
            var confidences = new List<float>();
            var probabilities = new List<float>();
            var boxes = new List<Rect2d>();

            var w = image.Width;
            var h = image.Height;
            /*
             YOLO3 COCO trainval output
             0 1 : center                    2 3 : w/h
             4 : confidence                  5 ~ 84 : class probability 
            */
            const int prefix = 5;   // 0 ~ 4 건너 뛰기

            foreach (var prob in output)
            {
                for (var i = 0; i < prob.Rows; i++)
                {
                    var confidence = prob.At<float>(i, 4);
                    if (confidence > threshold)
                    {
                        //get classes probability
                        Cv2.MinMaxLoc(prob.Row[i].ColRange(prefix, prob.Cols), out _, out Point max);
                        var classes = max.X;
                        var probability = prob.At<float>(i, classes + prefix);

                        if (probability > threshold) // 더 정확성, 당신은 그것을 취소 할 수 있습니다
                        {
                            //get center and width/height
                            var centerX = prob.At<float>(i, 0) * w;
                            var centerY = prob.At<float>(i, 1) * h;
                            var width = prob.At<float>(i, 2) * w;
                            var height = prob.At<float>(i, 3) * h;

                            if (!nms)
                            {
                                // 결과 그리기 (NMSBox를 사용하지 않는 경우)
                                Draw(image, classes, confidence, probability, centerX, centerY, width, height);
                                continue;
                            }

                            // NMSBox 목록에 데이터를 넣습니다.
                            classIds.Add(classes);
                            confidences.Add(confidence);
                            probabilities.Add(probability);
                            boxes.Add(new Rect2d(centerX, centerY, width, height));
                        }
                    }
                }
            }

            if (!nms) return;
            
            //비최대 억제를 사용하여 중복되는 저신뢰 상자 감소
            CvDnn.NMSBoxes(boxes, confidences, threshold, nmsThreshold, out int[] indices);

            Console.WriteLine($"NMSBoxes drop {confidences.Count - indices.Length} overlapping result.");

            foreach (var i in indices)
            {
                var box = boxes[i];
                Draw(image, classIds[i], confidences[i], probabilities[i], box.X, box.Y, box.Width, box.Height);
            }
            
        }

 

 

private static void Draw(Mat image, int classes,float confidence, float probability, double centerX, double centerY, double width, double height)
        {
            //라벨 형식
            var label = $"{Labels[classes]} {probability * 100:0.00}%";
            Console.WriteLine($"confidence {confidence * 100:0.00}% {label}");
            var x1 = (centerX - width / 2) < 0 ? 0 : centerX - width / 2; //avoid left side over edge
            //결과 그리기
            image.Rectangle(new Point(x1, centerY - height / 2), new Point(centerX + width / 2, centerY + height / 2), Colors[classes], 2);
            var textSize = Cv2.GetTextSize(label, HersheyFonts.HersheyTriplex, 0.5, 1, out var baseline);
            Cv2.Rectangle(image, new Rect(new Point(x1, centerY - height / 2 - textSize.Height - baseline),
                new Size(textSize.Width, textSize.Height + baseline)), Colors[classes], Cv2.FILLED);
            var textColor = Cv2.Mean(Colors[classes]).Val0 < 70 ? Scalar.White : Scalar.Black;
            Cv2.PutText(image, label, new Point(x1, centerY - height / 2 - baseline), HersheyFonts.HersheyTriplex, 0.5, textColor);
        }

//load model and config, if you got error: "separator_index < line.size()", check your cfg file, must be something wrong.
var net = CvDnn.ReadNetFromDarknet(cfg, model);
net.SetPreferableBackend(3);
/*
0:DNN_BACKEND_DEFAULT 
1:DNN_BACKEND_HALIDE 
2:DNN_BACKEND_INFERENCE_ENGINE
3:DNN_BACKEND_OPENCV 
 */
net.SetPreferableTarget(0);
/*
0:DNN_TARGET_CPU 
1:DNN_TARGET_OPENCL
2:DNN_TARGET_OPENCL_FP16
3:DNN_TARGET_MYRIAD 
4:DNN_TARGET_FPGA 
 */

prefer 옵션을 설정할 필요가 없으면 기본값을 사용하여 표시 할 수있는 두 개의 새로운 설정 명령이 있습니다.

//get output layer name
var outNames = net.GetUnconnectedOutLayersNames();

//create mats for output layer
Mat[] outs = outNames.Select(_ => new Mat()).ToArray();

//forward model
net.Forward(outs, outNames);

YOLO3는 단지 하나의 다중 출력 레이어를 가지지 않기 때문에 모든 출력 레이어를 찾아서 결과를 얻어야합니다. 이전과 같이 하나의 레이어를 얻는다면 왜 물체와 신뢰도가 낮은 지 궁금 할 것입니다.

//using non-maximum suppression to reduce overlapping low confidence box
CvDnn.NMSBoxes(boxes, confidences, threshold, nmsThreshold, out int[] indices);

foreach (var i in indices)
{
 var box = boxes[i];
 Draw(image, classIds[i], confidences[i], probabilities[i], box.X, box.Y, box.Width, box.Height);
}

여기에서 NMSBoxes를 사용하여 신뢰도가 낮은 겹치는 상자를 제거하면 끄고 켤 수 있습니다.

728x90

'자료' 카테고리의 다른 글

[Object Detection] Convert Darknet yolov3 model to keras model  (0) 2020.07.28
목업 작성시 사용하기 편한 사이트  (0) 2020.07.28
OpenCV 설치  (0) 2020.07.28
YOLO(Darknet) - 설치(GPU 설정)  (0) 2020.07.28
YOLO V3 cfg 파일 설정  (0) 2020.07.28