KUDZAI BOSHA
KUDZAI BOSHA

Reputation: 53

To convert a batch Labelme json files to a set of images and labels using anaconda prompt

I have a dataset of image masks that were generated by the Labelme tool as JSON files and on the Github tutorial (https://github.com/wkentaro/labelme/tree/master/examples/tutorial) it shows to change the JSON file into an image file we use the following command line code

labelme_json_to_dataset apc2016_obj3.json -o apc2016_obj3_json

however this only works for one file at a time, so I have been trying to find a way to process all the files using one set of code, I've tried the following code

setlocal
set "yourDir=C:\Users\Acer\Desktop\datasets\combined masks\"
set "yourExt=*.json"
pushd %yourDir%
for %%a in (*%yourExt%)do labelme_json_to_dataset %%a -o %%a
popd
endlocal

the code works now by reading the name of the file and adding the file extension .json however, saving the file to a directory with the same name including the .json extension is giving me the following error

Traceback (most recent call last):
  File "c:\users\acer\.conda\envs\labelme\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\users\acer\.conda\envs\labelme\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\ACER\.conda\envs\labelme\Scripts\labelme_json_to_dataset.exe\__main__.py", line 7, in <module>
  File "c:\users\acer\.conda\envs\labelme\lib\site-packages\labelme\cli\json_to_dataset.py", line 65, in main
    PIL.Image.fromarray(img).save(osp.join(out_dir, 'img.png'))
  File "c:\users\acer\.conda\envs\labelme\lib\site-packages\PIL\Image.py", line 2131, in save
    fp = builtins.open(filename, "w+b")
FileNotFoundError: [Errno 2] No such file or directory: '000828.json\\img.png'

I'm not familiar with cmd and require help saving the output to a directory with the file name without the .json extenstion

below is a single file example that shows how a successful run is supposed to look like enter image description here

(labelme) C:\Users\ACER\Desktop\datasets\combined masks>labelme_json_to_dataset 000814.json -o 000814
[[1m[33mWARNING[0m] [36mjson_to_dataset[0m:[36mmain[0m:[36m15[0m - [1m[33mThis script is aimed to demonstrate how to convert the JSON file to a single image dataset.[0m
[[1m[33mWARNING[0m] [36mjson_to_dataset[0m:[36mmain[0m:[36m17[0m - [1m[33mIt won't handle multiple JSON files to generate a real-use dataset.[0m
[[1m[37mINFO   [0m] [36mjson_to_dataset[0m:[36mmain[0m:[36m73[0m - [1m[37mSaved to: 000814[0m

(labelme) C:\Users\ACER\Desktop\datasets\combined masks>

Upvotes: 5

Views: 8203

Answers (5)

Gnnr
Gnnr

Reputation: 168

If you want to stick to Python and utilize the in-built functions, this is a minimal working example. It basically does the same thing as executing labelme_export_json via the command line for each json file individually.

import glob
import sys

import labelme.cli.export_json as export_json

sys.argv.append("")  # initial extension of sys.argv from one to two elements to allow sys.argv[1] = path_to_json below

paths_to_jsons = glob.glob(r"C:\myfolder\*.json")  # ADD YOUR PATH PATTERN HERE

for path_to_json in paths_to_jsons:
    sys.argv[1] = path_to_json  # modify arguments such that argparse can handle this
    export_json.main()  # function has knowledge of sys.argv and gets correct path from there via argparse

print(f"all done!")

(Of course, it can be extended for a proper use of argparse by the end user and an optional output directory, but for simplicity, this should be sufficient for most people.)

It needs to be saved as a .py-script and then executed; Python via command line will not work.

If you want to relabel and rerun your script, it is advisable to choose a different output folder, which can be stated using entries 2 and 3 of sys.argv. So, depending on your exact folder structure, code looks somewhat like this:

import glob
import sys
from pathlib import Path

import labelme.cli.export_json as export_json

[sys.argv.append(s) for s in ["", "-o", ""]]  # initial extension of sys.argv from one to four elements to allow sys.argv[2:4] = ... below

paths_to_jsons = glob.glob(r"C:\myfolder\*.json")  # ADD YOUR PATH PATTERN HERE

for path_to_json in paths_to_jsons:
    p = Path(path_to_json)
    out_path = p.parent.parent.parent / "labeled_images" / p.parts[-2] / p.stem  # ADJUST TO YOUR NEEDS HERE
    out_path.mkdir(exist_ok=True, parents=True)
    sys.argv[1] = str(path_to_json)  # modify arguments such that argparse can handle this
    sys.argv[3] = str(out_path)  # modify arguments such that argparse can handle this
    export_json.main()  # function has knowledge of sys.argv and gets correct path from there via argparse

print(f"all done!")

Upvotes: 0

TheCodeNovice
TheCodeNovice

Reputation: 692

This solution is not perfect but just produces the mask and overlay png file. Also it just preserves the file name.

import argparse
import base64
import json
import os
import os.path as osp

import imgviz
import PIL.Image

from labelme.logger import logger
from labelme import utils


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("json_file")
    parser.add_argument("-o", "--out", default=None)
    args = parser.parse_args()

    json_file = args.json_file

    if args.out is None:
        out_dir = osp.basename(json_file).replace(".", "_")
        out_dir = osp.join(osp.dirname(json_file), out_dir)
    else:
        out_dir = args.out
    if not osp.exists(out_dir):
        os.mkdir(out_dir)

    data = json.load(open(json_file))
    imageData = data.get("imageData")

    if not imageData:
        imagePath = os.path.join(os.path.dirname(json_file), data["imagePath"])
        print(imagePath)
        with open(imagePath, "rb") as f:
            imageData = f.read()
            imageData = base64.b64encode(imageData).decode("utf-8")
    img = utils.img_b64_to_arr(imageData)
    label_name_to_value = {"_background_": 0}
    for shape in sorted(data["shapes"], key=lambda x: x["label"]):
        label_name = shape["label"]
        if label_name in label_name_to_value:
            label_value = label_name_to_value[label_name]
        else:
            label_value = len(label_name_to_value)
            label_name_to_value[label_name] = label_value
    lbl, _ = utils.labelme_shapes_to_label(img.shape, data["shapes"])

    label_names = [None] * (max(label_name_to_value.values()) + 1)
    for name, value in label_name_to_value.items():
        label_names[value] = name

    lbl_viz = imgviz.label2rgb(
        lbl, imgviz.asgray(img), label_names=label_names, loc="rb"
    )

    filename = str(json_file).split('.')[1]
    utils.lblsave(osp.join(out_dir, f'.{filename}.png'), lbl)
    PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, f'.{filename}_viz.png'))

    logger.info("Saved to: {}".format(out_dir))


if __name__ == "__main__":
    main()

Upvotes: 0

salapao
salapao

Reputation: 1

for /l %n in (2,1,50) do 
    (labelme_json_to_dataset images%n.json -o images%n)

Every file.json and every blank folder (that's prepared before for saving dataset) are in the same folder (current dir for use this code).

Upvotes: 0

Devshree Patel
Devshree Patel

Reputation: 19

import labelme
import os, sys
path="path/to/directory"
dirs = os.listdir(path)
i=0
for item in dirs:
   if item.endswith(".json"):
      if os.path.isfile(path+item):
         my_dest ="fin" + str(i)
         os.system("mkdir "+my_dest)
         os.system("labelme_json_to_dataset "+item+" -o "+my_dest)
         i=i+1

Upvotes: 1

lit
lit

Reputation: 16256

Use the command FOR /? to read about substitution of FOR variable references on the last page of the help output. To get just the file basename, %%~na can be used. Run this without ECHO OFF so that you can see each command.

setlocal
set "yourDir=C:\Users\Acer\Desktop\datasets\combined masks\"
set "yourExt=*.json"
pushd %yourDir%
for %%a in (*%yourExt%) do (labelme_json_to_dataset %%a -o %%~na)
popd
endlocal

Upvotes: 1

Related Questions