C#本地将labelme数据集转换为机器视觉yolo数据集格式

发布于:2025-03-20 ⋅ 阅读:(12) ⋅ 点赞:(0)

C#本地,将labelme数据集转换为机器视觉yolo数据集格式

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;
using System.Text.Unicode;
using System.Threading.Tasks;
using System.Windows.Ink;
using System.Windows.Media.Media3D;

namespace WpfAppYolo11_test1
{
    /// <summary>
    /// 将labelme数据集转换为yolo数据集格式
    /// </summary>
    public class LabelmeToYoloDataSetUtils
    {
        Dictionary<string, short> labels = new Dictionary<string, short>();


        /// <summary>
        /// 转换后保存目录
        /// </summary>
        const string outDir = "D:\\yoloTrainDataSet";
        const int saveLength = 10;

        /// <summary>
        /// labelme转yolo格式数据,要转换的目录
        /// </summary>
        /// <param name="inputDir"></param>
        /// 2025-3-4 00:45:38,
        public void FileConvert(string inputDir)
        {
            //labels.Clear();
            var files = System.IO.Directory.GetFiles(inputDir, "*.json");
            if (files == null || files.Length == 0)
            {
                return;
            }


            //获取目录中文件夹,以数字升序命名最新的训练文件夹
            var folders = Directory.GetDirectories(outDir);
            int lastNum = 1;
            if (folders != null && folders.Length > 0)
            {
                List<DirectoryInfo> dirList = new List<DirectoryInfo>();
                foreach (var item in folders)
                {
                    DirectoryInfo directoryInfo = new DirectoryInfo(item);
                    dirList.Add(directoryInfo);
                }
                var lastDir = dirList.OrderByDescending(g => g.CreationTime).FirstOrDefault();
                //获取文件夹后缀
                var lastNumStr = Regex.Match(lastDir.Name, "\\d*$").Value;
                lastNum = Convert.ToInt32(lastNumStr) + 1;
            }
            //此次训练保存的文件夹
            string newTrainDir = "train" + lastNum;

            //string dirNew = "train" + Guid.NewGuid().ToString("n");
            string dirNew = outDir + "\\" + newTrainDir + "\\train";
            //处理json文件
            foreach (var item in files)
            {
                //处理单个json文件
                SingleFileConvert(item, dirNew);
            }

            //挑选训练图片前两个图片作为验证集
            //创建val文件夹
            string val = Path.Combine(outDir, newTrainDir, "val");                      
           
            var files21 = System.IO.Directory.GetFiles(inputDir, "*.jpg");
            var files22 = System.IO.Directory.GetFiles(inputDir, "*.png");
            var fileContainer = files21.Union(files22).ToList();
            var first1 = fileContainer[0];
            var first2 = fileContainer[1];

            //将训练图片全部复制到train中
            var trainImgNew = Path.Combine(dirNew, "images");
            if (!Directory.Exists(trainImgNew))
            {
                Directory.CreateDirectory(trainImgNew);
            }
            foreach (var itemImg in fileContainer)
            {
                string fileNew2  = Path.Combine(trainImgNew, Path.GetFileName(itemImg));
                File.Copy(itemImg, fileNew2);
            }
            //只有2个样本时,将训练的文件夹作为验证
            if (files.Length <= 2)
            {
                val = dirNew;
                goto Val_done;
            }
            if (!Directory.Exists(val))
            {
                Directory.CreateDirectory(val);
            }
            //图片文件夹
            string valImage = Path.Combine(val, "images");
            if (!Directory.Exists(valImage))
            {
                Directory.CreateDirectory(valImage);
            }
            string file1Name = Path.GetFileName(first1);
            string file2Name = Path.GetFileName(first2);
            string img1 = Path.Combine(valImage, file1Name);
            File.Copy(first1, img1);
            string img2 = Path.Combine(valImage, file2Name);
            File.Copy(first2, img2);

            //坐标数据
            string vallabels = Path.Combine(val, "labels");
            if (!Directory.Exists(vallabels))
            {
                Directory.CreateDirectory(vallabels);
            }
            //找到train目录中的坐标数据
            string vallabels2 = Path.Combine(dirNew, "labels");
            string txt1 = Path.GetFileNameWithoutExtension(file1Name) + ".txt";
            string txt2 = Path.GetFileNameWithoutExtension(file2Name) + ".txt";
            string fileLabel1 = Path.Combine(vallabels2, txt1);
            string fileLabel2 = Path.Combine(vallabels2, txt2);
            if (File.Exists(fileLabel1))
            {
                string fileLabelTxt1 = Path.Combine(vallabels, txt1);
                File.Copy(fileLabel1, fileLabelTxt1);
            }
            if (File.Exists(fileLabel2))
            {
                string fileLabelTxt2 = Path.Combine(vallabels, txt2);
                File.Copy(fileLabel2, fileLabelTxt2);
            }

        Val_done:
            //生成data.yaml
            string dir = dirNew;//Path.Combine(outDir, newTrainDir);
            dir = dir.Replace("\\", "/");
            val = val.Replace("\\", "/");

            StringBuilder sb = new StringBuilder();
            sb.AppendLine("train: " + dir);
            sb.AppendLine("val: " + val);
            sb.AppendLine("test: " + val);
            sb.AppendLine("");
            sb.AppendLine("nc: " + labels.Count);

            //设置编码支持中文
            string labelName = System.Text.Json.JsonSerializer.Serialize(labels.Keys,
                     new System.Text.Json.JsonSerializerOptions()
                     {
                         Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
                     }
                );
            labelName = labelName.Replace("\"", "'");//替换双引号为单引号
            sb.AppendLine("names: " + labelName);

            //保存
            //var arr = inputDir.Split("\\");
            //var lastDir = arr.Last();
            //string newDir = inputDir.TrimEnd(lastDir.ToArray());
            //string filePath = Path.Combine(newDir, "yoloTrainDataSet", "data.yaml");
            string yamlDir = outDir + "\\" + newTrainDir;
            string filePath = Path.Combine(yamlDir, "data.yaml");
            System.IO.File.WriteAllText(filePath, sb.ToString());


        }

        /// <summary>
        /// 单个json文件转换
        /// </summary>
        public void SingleFileConvert(string file, string inputDir)
        {
            string json = System.IO.File.ReadAllText(file);
            var data2 = System.Text.Json.JsonSerializer.Deserialize<LabelMeView>(json);
            if (data2 == null || data2.shapes == null || data2.shapes.Count == 0)
            {
                return;
            }
            StringBuilder stringBuilder = new StringBuilder();

            //读取每个形状
            foreach (var item in data2.shapes)
            {
                //忽略没名称的形状
                if (string.IsNullOrEmpty(item.label))
                {
                    continue;
                }
                labels.TryAdd(item.label, 1);

                double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
                if (item.shape_type == "rectangle")
                {
                    x1 = item.points[0][0];
                    y1 = item.points[0][1];

                    x2 = item.points[1][0];
                    y2 = item.points[1][1];
                }
                else if (item.shape_type == "polygon")
                {
                    x1 = item.points.Select(g => g[0]).Min();
                    y1 = item.points.Select(g => g[1]).Min();

                    x2 = item.points.Select(g => g[0]).Max();
                    y2 = item.points.Select(g => g[1]).Max();
                }
                //中心点
                double x_center = (x1 + x2) / (double)2 / data2.imageWidth;
                double y_center = (y1 + y2) / (double)2 / data2.imageHeight;
                double width = Math.Abs((x2 - x1) / data2.imageWidth);
                double height = Math.Abs((y2 - y1) / data2.imageHeight);
                 
                //获取对象id
                int classId = labels.Keys.ToList().IndexOf(item.label);

                stringBuilder.AppendLine($"{classId} {x_center} {y_center} {width} {height}");
            }
            //保存txt文件
            //var arr = inputDir.Split("\\");
            //var lastDir = arr.Last();
            //string newDir = inputDir.TrimEnd(lastDir.ToArray());
            // string dir = System.IO.Path.Combine( outDir, "labels");
            string dir = System.IO.Path.Combine(inputDir, "labels");
            if (!System.IO.Directory.Exists(dir))
            {
                System.IO.Directory.CreateDirectory(dir);
            }
            //string fileName = Path.GetFileName(file) + "_" + Guid.NewGuid().ToString("n") + ".txt";
            string fileName = Path.GetFileNameWithoutExtension(file) + ".txt";
            string filePath = System.IO.Path.Combine(dir, fileName);
            File.WriteAllText(filePath, stringBuilder.ToString());
        }
    }

    public class LabelMeView
    {
        public string version { get; set; }
        public List<ShapeView> shapes { get; set; }
        public string imagePath { get; set; }

        /// <summary>
        /// 图片高度,px
        /// </summary>
        public int imageHeight { get; set; }

        /// <summary>
        /// 图片宽度,px
        /// </summary>
        public int imageWidth { get; set; }
    }

    public class ShapeView
    {
        /// <summary>
        /// 对象说明,标签
        /// </summary>
        public string label { get; set; }

        /// <summary>
        /// 形状描述;rectangle;polygon
        /// </summary>
        public string shape_type { get; set; }
        public object flags { get; set; }

        /// <summary>
        /// 坐标点
        /// </summary>
        public List<double[]> points { get; set; }
        public string version { get; set; }
    }

}