[C#]根据labelme产生的json文件,批量生成8位黑白png图

目录

背景

软件环境

黑白图像素填充原理

源码

示例图片

附录:

 1.调试带参数的控制台应用程序方法

 2. 使用pyinstaller将labelme打包为exe文件

 3. 数据标注软件labelme详解


 

背景

由于工作原因,需要对图像进行分类,建模,作为深度学习的材料。自己造轮子造了半天,结果发现labelme这块做的很好,于是放弃造轮子,借用labelme产生的json文件,生成需要的8位黑白png图(labelme自带的生成8位png图是彩色的,不满足需求)。

软件环境

Win10  python3.8.2    labelme4.2.10  pyqt5.14.1  pip-20.0.2

黑白图像素填充原理

根据json文件中的label标签,随机生成不同的1-255之间的数值,作为region的像素值。背景像素全为0

源码

解析JSON文件,我添加了Newtonsoft.Json.dll引用。可以直接在NuGet中搜索添加。

System.Drawing也需要在“程序集-框架”中添加。

以下为程序源码

Program.cs文件

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

using Newtonsoft.Json;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;


namespace labelme_Json_to_png8
{
    class Program
    {
        /// <summary>
        /// 根据labelme-4.2.10产生的json文件批量生成png图片
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            if(args.Length==2)
            {
                string strPathSrc = args[0];  //labelme产生的JSON文件所在文件夹
                string strPathDes = args[1];  //产生的PNG文件存放的文件夹

                try
                {
                    List<string> srcFileNames = labelmeJson.GetJsonfiles(strPathSrc);

                    int srcNum = srcFileNames.Count;
                    for (int i = 0; i < srcNum; i++)
                    {
                        string strDes = Path.Combine(strPathDes, Path.GetFileNameWithoutExtension(srcFileNames[i])+".png");
                        //desFileNames.Add(str);
                        labelmeJson.CreateImage(srcFileNames[i], strDes);
                    }
                    Console.WriteLine("Convert OK! 目标文件夹地址:" + strPathDes);
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
                Console.ReadKey();
            }
        }
    }
}

labelmeJson.cs文件

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

using Newtonsoft.Json;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace labelme_Json_to_png8
{
   public class labelmeJson
    {
        private static PointF[] GetPointFs(List<List<double>> listDou)
        {
            int inum = listDou.Count;
            PointF[] pointf = new PointF[inum];
            for (int i = 0; i < inum; i++)
            {
                PointF p = new PointF();
                p.X = (float)listDou[i][0];
                p.Y = (float)listDou[i][1];
                pointf[i] = p;
            }
            return pointf;
        }
       /// <summary>
       /// 随机给各个region赋1-255之间像素值,背景色像素值为0
       /// </summary>
       /// <param name="strSourcePath"></param>
       /// <param name="strDesPath"></param>
       /// <returns></returns>
        public static bool CreateImage(string strSourcePath, string strDesPath)
        {
            try
            {
                string jsonstr = File.ReadAllText(strSourcePath);
                LabelmeJsonObject lbjs = JsonConvert.DeserializeObject<LabelmeJsonObject>(jsonstr);
                //确保图像的宽度是4的倍数
                int imageWidth = int.Parse(lbjs.imageWidth);
                int imageHeight = int.Parse(lbjs.imageHeight);

                Bitmap bit = new Bitmap(imageWidth, imageHeight);
                Graphics dc = Graphics.FromImage(bit);
                dc.Clear(Color.Black);

                Dictionary<string, int> dicLabel_Val = new Dictionary<string, int>();

                Random random = new Random();
                foreach (Shapes shape in lbjs.shapes)
                {
                    string label = shape.label;                    
                    if (!dicLabel_Val.Keys.Contains(label))
                    {
                        int num = random.Next(1, 255);
                        dicLabel_Val.Add(label, num);
                    }
                }

                foreach (Shapes shape in lbjs.shapes)
                {
                    int rval = dicLabel_Val[shape.label];
                    if (shape.shape_type == "polygon")
                    {
                        PointF[] pointf = GetPointFs(shape.points);
                        dc.FillPolygon(new SolidBrush(Color.FromArgb(rval, 0, 0)), pointf);
                        dc.Save();
                    }
                    else if (shape.shape_type == "linestrip")
                    {
                        PointF[] pointf = GetPointFs(shape.points);
                        dc.DrawLines(new Pen(new SolidBrush(Color.FromArgb(rval, 0, 0))), pointf);
                        dc.Save();
                    }
                    else if (shape.shape_type == "rectangle")
                    {
                        PointF[] pointf = GetPointFs(shape.points);
                        Rectangle rect = new Rectangle((int)pointf[0].X, (int)pointf[0].Y, (int)(pointf[1].X - pointf[0].X), (int)(pointf[1].Y - pointf[0].Y));
                        dc.FillRectangle(new SolidBrush(Color.FromArgb(rval, 0, 0)), rect);
                        dc.Save();
                    }
                    else if (shape.shape_type == "circle")
                    {
                        PointF[] pointf = GetPointFs(shape.points);
                        double f = Math.Pow((pointf[1].X - pointf[0].X), 2) + Math.Pow((pointf[1].Y - pointf[0].Y), 2);
                        float r = (float)Math.Sqrt(f);
                        dc.FillEllipse(new SolidBrush(Color.FromArgb(rval, 0, 0)), pointf[0].X, pointf[0].Y, r, r);
                        dc.Save();
                    }
                    else if (shape.shape_type == "point")
                    {
                        PointF[] pointf = GetPointFs(shape.points);
                        dc.FillEllipse(new SolidBrush(Color.FromArgb(rval, 0, 0)), pointf[0].X, pointf[0].Y, 2, 2);
                        dc.Save();
                    }
                }


                Bitmap bmp = RgbToR8(bit);
                bmp.Save(strDesPath, ImageFormat.Png);

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }
            return true;
        }

        /// <summary>
        /// 获取当前目录下所有的json文件
        /// </summary>
        /// <param name="strPath"></param>
        /// <returns></returns>
        public static List<string> GetJsonfiles(string strPath)
        {
            List<string> list = new List<string>();
            DirectoryInfo theFolder = new DirectoryInfo(strPath);  // 给出你的目录文件位置 


            FileInfo[] fileInfo = theFolder.GetFiles(); // 获得当前的文件夹内的所有文件数组


            foreach (FileInfo NextFile in fileInfo)   //遍历文件
            {
                if (NextFile.Extension.ToLower() == ".json")  // 得到你想要的格式
                {
                    list.Add(NextFile.FullName); // 用于测试输出
                }
            }
            return list;
        }

        /// <summary>
        /// 取源图像R通道数据,并转化为8位单像素图像。
        /// </summary>
        /// <param name="original"> 源图像。 </param>
        /// <returns> 8位单像素图像。 </returns>
        public static Bitmap RgbToR8(Bitmap original)
        {
            if (original != null)
            {
                // 将源图像内存区域锁定
                Rectangle rect = new Rectangle(0, 0, original.Width, original.Height);
                BitmapData bmpData = original.LockBits(rect, ImageLockMode.ReadOnly,
                        PixelFormat.Format24bppRgb);

                // 获取图像参数
                int width = bmpData.Width;
                int height = bmpData.Height;
                int stride = bmpData.Stride;  // 扫描线的宽度,比实际图片要大
                int offset = stride - width * 3;  // 显示宽度与扫描线宽度的间隙
                IntPtr ptr = bmpData.Scan0;   // 获取bmpData的内存起始位置的指针                
                int scanBytesLength = stride * height;  // 用stride宽度,表示这是内存区域的大小

                // 分别设置两个位置指针,指向源数组和目标数组
                int posScan = 0, posDst = 0;
                byte[] rgbValues = new byte[scanBytesLength];  // 为目标数组分配内存
                Marshal.Copy(ptr, rgbValues, 0, scanBytesLength);  // 将图像数据拷贝到rgbValues中
                // 分配单像素数组
                byte[] grayValues = new byte[width * height]; // 不含未用空间。
                // 计算单像素数组
                byte blue, green, red;

                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {

                        blue = rgbValues[posScan];
                        green = rgbValues[posScan + 1];
                        red = rgbValues[posScan + 2];
                        grayValues[posDst] = red;
                        posScan += 3;
                        posDst++;

                    }
                    // 跳过图像数据每行未用空间的字节,length = stride - width * bytePerPixel
                    posScan += offset;
                }

                // 内存解锁
                Marshal.Copy(rgbValues, 0, ptr, scanBytesLength);
                original.UnlockBits(bmpData);  // 解锁内存区域

                // 构建8位单像素位图
                Bitmap retBitmap = BuiltGrayBitmap(grayValues, width, height);
                return retBitmap;
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 用数组新建一个8位图像。
        /// </summary>
        /// <param name="rawValues"> 像素数组(length = width * height)。 </param>
        /// <param name="width"> 图像宽度。 </param>
        /// <param name="height"> 图像高度。 </param>
        /// <returns> 新建的8位单像素位图。 </returns>
        private static Bitmap BuiltGrayBitmap(byte[] rawValues, int width, int height)
        {
            // 新建一个8位位图,并锁定内存区域操作
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height),
                 ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);

            // 计算图像参数
            int offset = bmpData.Stride - bmpData.Width;        // 计算每行未用空间字节数
            IntPtr ptr = bmpData.Scan0;                         // 获取首地址
            int scanBytes = bmpData.Stride * bmpData.Height;    // 图像字节数 = 扫描字节数 * 高度
            byte[] grayValues = new byte[scanBytes];            // 为图像数据分配内存

            // 为图像数据赋值
            int posSrc = 0, posScan = 0;                        // rawValues和grayValues的索引
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    grayValues[posScan++] = rawValues[posSrc++];
                }
                // 跳过图像数据每行未用空间的字节,length = stride - width * bytePerPixel
                posScan += offset;
            }

            // 内存解锁
            Marshal.Copy(grayValues, 0, ptr, scanBytes);
            bitmap.UnlockBits(bmpData);  // 解锁内存区域

            // 修改生成位图的索引表,从伪彩修改为单像素
            ColorPalette palette;
            // 获取一个Format8bppIndexed格式图像的Palette对象
            using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
            {
                palette = bmp.Palette;
            }
            for (int i = 0; i < 256; i++)
            {
                palette.Entries[i] = Color.FromArgb(i, i, i);
            }
            // 修改生成位图的索引表
            bitmap.Palette = palette;

            return bitmap;
        }
    }


   public class Flags { }   
   public class Shapes
   {
       public string label { get; set; }
       public List<List<double>> points { get; set; }
       public string group_id { get; set; }
       public string shape_type { get; set; }
       public Flags flags { get; set; }
   }

   public class LabelmeJsonObject
   {
       public string version { get; set; }
       public Flags flags { get; set; }
       public List<Shapes> shapes { get; set; }
       public string imagePath { get; set; }
       public string imageData { get; set; }
       public string imageHeight { get; set; }
       public string imageWidth { get; set; }
   }
}

示例图片

附录:

 1.调试带参数的控制台应用程序方法

 在项目属性中选中 “调试” 页,在 “启动选项-命令行参数”栏中写入 json文件所在文件夹 和 产生的png文件保存的文件夹。和在cmd中输入参数格式一样,注意其中的空格。例如:d:/img/json d:/img/json/png

 在“启动选项-工作目录”栏中,选择程序所在目录,例如:程序的debug目录。

 2. 使用pyinstaller将labelme打包为exe文件

https://blog.csdn.net/weixin_44001860/article/details/103746555

 3. 数据标注软件labelme详解

https://blog.csdn.net/u014061630/article/details/88756644

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
可以使用labelme的官方工具labelme2voc将json文件转化为VOC格式的标注文件,然后使用VOC格式的标注文件生成灰度的png像。 具体步骤如下: 1. 安装labelme2voc 可以使用pip安装: ``` pip install labelme2voc ``` 2. 将json文件转化为VOC格式的标注文件 使用labelme2voc命令将json文件转化为VOC格式的标注文件: ``` labelme2voc input_json_dir output_voc_dir ``` 其中,input_json_dir是存放json文件的目录路径,output_voc_dir是输出VOC格式标注文件的目录路径。 例如,有一个json文件存放在/home/user/data/json目录下,要将其转化为VOC格式标注文件并存放在/home/user/data/voc目录下,可以执行以下命令: ``` labelme2voc /home/user/data/json /home/user/data/voc ``` 执行完毕后,会在输出目录下生成json文件对应的VOC格式标注文件。 3. 将VOC格式标注文件生成灰度的png像 可以使用VOC格式标注文件生成灰度的png像的工具有很多,例如opencv、PIL等。 以opencv为例,可以使用以下代码将VOC格式标注文件生成灰度的png像: ``` import cv2 import os input_voc_dir = "/home/user/data/voc" output_gray_dir = "/home/user/data/gray" if not os.path.exists(output_gray_dir): os.makedirs(output_gray_dir) for filename in os.listdir(input_voc_dir): if filename.endswith(".xml"): xml_file = os.path.join(input_voc_dir, filename) img_file = xml_file.replace(".xml", ".jpg") img = cv2.imread(img_file) h, w = img.shape[:2] gray = np.zeros((h, w), np.uint8) tree = ET.parse(xml_file) for obj in tree.findall("object"): name = obj.find("name").text bndbox = obj.find("bndbox") xmin = int(bndbox.find("xmin").text) ymin = int(bndbox.find("ymin").text) xmax = int(bndbox.find("xmax").text) ymax = int(bndbox.find("ymax").text) gray[ymin:ymax, xmin:xmax] = 255 if name == "target" else 128 gray_file = os.path.join(output_gray_dir, filename.replace(".xml", ".png")) cv2.imwrite(gray_file, gray) ``` 其中,input_voc_dir是存放VOC格式标注文件的目录路径,output_gray_dir是输出灰度png像的目录路径。 该代码会遍历目录下所有的xml文件(即VOC格式标注文件),根据标注信息生成相应的灰度的png像,并存放在输出目录下。其中,目标区域的像素值为255,非目标区域的像素值为128。 执行完毕后,会在输出目录下生成与VOC格式标注文件对应的灰度png像。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值