인공지능

[yolo] darknet format 다크넷 포맷으로 변환하기

Heeyeon Choi 2024. 1. 5. 14:24
728x90

darknet format (다크넷 포맷) 은 

 

클래스아이디, 바운딩 박스 중심 x좌표, 바운딩 박스 중심 y좌표, width, height 이다. (각각 resolution 값으로 나눠줘야함)

 

예시)

1 0.2 0.3 0.2 0.3 

 

- 변환하려는 데이터

https://aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&aihubDataSe=realm&dataSetSn=176

 

AI-Hub

샘플 데이터 ? ※샘플데이터는 데이터의 이해를 돕기 위해 별도로 가공하여 제공하는 정보로써 원본 데이터와 차이가 있을 수 있으며, 데이터에 따라서 민감한 정보는 일부 마스킹(*) 처리가 되

aihub.or.kr

 

AI Hub의 데이터셋 라벨링 데이터는 보통 darknet format 으로 안 되어 있다. 변환을 해보자!

 

라벨링 파일 예시)

{
  "image": {
    "date": "20201126",
    "path": "fire/Train/image",
    "filename": "S3-N0806MF07391.jpg",
    "copyrighter": "미디어그룹사람과숲(컨)",
    "H_DPI": 96,
    "location": "08",
    "V_DPI": 96,
    "bit": 24,
    "resolution": [
      1920,
      1080
    ]
  },
  "annotations": [
    {
      "data ID": "S3",
      "middle classification": "01",
      "flags": "not occluded, not truncated",
      "box": [
        557,
        446,
        1052,
        860
      ],
      "class": "02"
    }
  ]
}

 

해당 데이터셋의 구조 설명서를 읽어보면, box 좌표는 x1, y1, x2, y2 로 이뤄져 있다.

darknet format 으로 변경하기 위해서는

 

double xMiddle = (x2 + x1) / 2 / xRes;
double yMiddle = (y2 + y1) / 2 / yRes;
double width = (x2 - x1) / xRes;
double height = (y2 - y1) / yRes;

 

class, xMiddle , yMiddle  , width  , height   좌표로 변환해줘야 한다. 

 

또, 변환하는데 필요한 프로퍼티

  • image- filename
  • annotations - box
  • annotations - class

가 있다.

 

데이터 학습시킬 때, 이미지와 어노테이션의 파일명이 같아야 하므로, 주의한다.

또한, 하나의 이미지에 여러개의 객체가 존재하거나, null 만 존재할 수 있다. 이를, 코드에 다 녹여야 한다.

 

라벨링 파일 예시)  데이터의 클래스 목록

 

01 클래스를 0 값으로 치환해주고 나머지도 순서대로 값을 매겨준다.

 

json 데이터를 클래스로 작성

https://json2csharp.com/

 

Convert JSON to C# Classes Online - Json2CSharp Toolkit

 

json2csharp.com

에서 쉽게 코드를 받았다. 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnnotationToDarknetFormat
{// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
    public class Annotation
    {
        public string dataID { get; set; }
        public string middleclassification { get; set; }
        public string flags { get; set; }
        public List<int> box { get; set; }
        public List<List<int>> polygon { get; set; }
        public string @class { get; set; }
    }

    public class Image
    {
        public string date { get; set; }
        public string path { get; set; }
        public string filename { get; set; }
        public string copyrighter { get; set; }
        public int H_DPI { get; set; }
        public string location { get; set; }
        public int V_DPI { get; set; }
        public int bit { get; set; }
        public List<int> resolution { get; set; }
    }

    public class Root
    {
        public Image image { get; set; }
        public List<Annotation> annotations { get; set; }
    }


}

 

- 변환코드)

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnnotationToDarknetFormat
{
     class mainClass
    {
        static void Main(string[] args)
        {
            try
            {
                string rootPath = @"G:\fireProject_240105\화재 발생 예측 영상\Training\[라벨]유사씬\"; //json을 읽어올 경로
                string[] files = Directory.GetFiles(rootPath, "*.json", SearchOption.AllDirectories);
                string txtSavePath = rootPath + @"txt\";
                DirectoryInfo dir = new DirectoryInfo(txtSavePath);
                if (dir.Exists == false)
                {
                    dir.Create();
                }

                foreach (var file in files)
                {
                    if (File.Exists(file))
                    {
                        
                        string txt = "";//텍스트 파일에 들어갈 내용, 저장 후 초기화 필요
                        string json = File.ReadAllText(file);
                        Root fileDatas = JsonConvert.DeserializeObject<Root>(json);
                        
                        if (fileDatas != null)  // null 가능성 고려 
                        {

                            string fileName = fileDatas.image.filename;
                            string txtFileName = txtSavePath + fileName.Substring(0, fileName.IndexOf(".")) + ".txt";
                            if(File.Exists(txtFileName) == false)
                            {
                                double xRes = fileDatas.image.resolution[0];
                                double yRes = fileDatas.image.resolution[1];

                                if (fileName != null && xRes != 0 && yRes != 0  )
                                {
                                    for (int i = 0; i < fileDatas.annotations.Count; i++) //각 이미지 당 여러 객체 존재가능
                                    {
                                        if(fileDatas.annotations[i] != null&& fileDatas.annotations[i].polygon ==null && fileDatas.annotations[i].box !=null)
                                        {
                                            int x1 = 0;
                                            int y1 = 0;
                                            int x2 = 0;
                                            int y2 = 0;

                                            
                                            x1 = fileDatas.annotations[i].box[0];
                                            y1 = fileDatas.annotations[i].box[1];
                                            x2 = fileDatas.annotations[i].box[2];
                                            y2 = fileDatas.annotations[i].box[3];

                                            string classID = fileDatas.annotations[i].@class;

                                            if (classID != null && x1 != 0 && y1 != 0 && x2 != 0 && y2 != 0)
                                            {

                                                double xMiddle = (x2 + x1) / 2 / xRes;
                                                double yMiddle = (y2 + y1) / 2 / yRes;
                                                double width = (x2 - x1) / xRes;
                                                double height = (y2 - y1) / yRes;

                                                switch (classID)
                                                {
                                                    case "01":
                                                        classID = "0";
                                                        break;
                                                    case "02":
                                                        classID = "1";
                                                        break;
                                                    case "03":
                                                        classID = "2";
                                                        break;
                                                    case "04":
                                                        classID = "3";
                                                        break;
                                                    case "05":
                                                        classID = "4";
                                                        break;
                                                    case "06":
                                                        classID = "5";
                                                        break;
                                                    case "07":
                                                        classID = "6";
                                                        break;
                                                    case "08":
                                                        classID = "7";
                                                        break;
                                                    case "09":
                                                        classID = "8";
                                                        break;
                                                    case "10":
                                                        classID = "9";
                                                        break;
                                                    case "11":
                                                        classID = "10";
                                                        break;
                                                    default:
                                                        break;

                                                }

                                                txt += classID + " " + xMiddle + " " + yMiddle + " " + width + " " + height + '\n';
                                            }
                                        
                                            

                                        }
                                    }

                                    File.WriteAllText(txtFileName, txt, Encoding.Default);
                                }

                            }

                        }
                    }

                }
            }
            catch (Exception ex)
            { 
                
            }


            
        }
    }
}

- json 데이터를 받아오고, DeserializeObject 해준다.

- 각 클래스에 맞는 필요한 값들을 변수로 저장해준다.

- 공식에 알맞게 값을 변환하고 txt 에 저장해준다.

- 실제 text 파일에 저장해준다. 

- 파일이름은 이미지, json 과 동일하게 해준다.

728x90