System designHow-to

Convert JSON bounding box annotations for vision training

Convert JSON bounding box annotations into normalized training labels, with coordinate formulas, class mapping, Python checks and overlay validation.

LumaSpec Vision9 min read Share on LinkedIn

On this page 10 sections
  1. 01Define the annotation contract first
  2. 02Join each record to its actual image dimensions
  3. 03Calculate normalized centers and sizes
  4. 04Implement a converter that rejects ambiguous records
  5. 05Write paired files without losing image identity
  6. 06Reconstruct boxes and inspect overlays
  7. 07Audit empty images, counts and dataset splits
  8. 08Keep acquisition choices separate from annotation conversion
  9. 09Run the final release checklist
  10. 10Questions

A JSON annotation file can contain valid rectangles without being ready for a training loader. The converter must preserve image identity, distinguish class labels from validity flags, and express every box in the coordinate system expected downstream. This procedure is for teams preparing rectangular object annotations for machine-vision training. You will define the input contract, calculate normalized labels, reject questionable records, and verify the conversion with overlays before starting training.

Define the annotation contract first

  1. Confirm what every field means before writing conversion code.

For this procedure, assume each image record has an image identifier and a list of rectangles:

{
  "img_name": "cell_014",
  "ann": [[160, 240, 480, 600, 1]]
}

The rectangle contains x_min, y_min, x_max, y_max, and a validity flag. Coordinates are measured from the image's top-left corner, with horizontal positions increasing rightward and vertical positions increasing downward.

The destination format used here has one object per line:

class_id x_center_normalized y_center_normalized width_normalized height_normalized

This is a specific text-label format, not a universal requirement for every training system. Confirm that your destination loader expects these fields, their order, and normalized coordinates.

A validity flag is not automatically a class identifier. If the final value means “accepted annotation,” copying it into the class column changes its meaning. For a verified single-class dataset, assign the configured class identifier independently. For multiple classes, obtain the category from documented metadata or an explicit mapping.

Join each record to its actual image dimensions

  1. Build a manifest connecting every annotation record to exactly one image.

For each image, record its resolved filename, width, height, annotation identifier, and applicable class mapping. Use dimensions from the actual image supplied to training, not a camera setting or an assumed dataset-wide size.

Check for duplicate identifiers, missing images, and multiple extensions sharing the same stem. An identifier such as cell_014 is insufficient when both a full image and a cropped version use that name in different directories.

Keep the conversion focused on geometry. Image decoding and pixel-array checks belong in a separate validation stage; the guide to validating camera frames before image conversion explains that boundary.

The annotations and dimensions must describe the same orientation and processing stage. Rotation, cropping, and padding change the coordinate frame. A pure resize preserves normalized coordinates only when the image and annotations undergo the same scaling, without additional cropping or padding.

Also document the endpoint convention. This example treats corners as continuous image-edge coordinates, allowing a right edge equal to the image width. An annotation system using inclusive integer endpoints needs a documented conversion rule before applying these formulas.

Calculate normalized centers and sizes

  1. Convert corner coordinates into centers and extents, then normalize each axis separately.

Let W be image width and H be image height. The following expressions assume ordered corners and positive image dimensions:

box_width = x_max - x_min box_height = y_max - y_min x_center = (x_min + x_max) / 2 y_center = (y_min + y_max) / 2 x_center_normalized = x_center / W y_center_normalized = y_center / H width_normalized = box_width / W height_normalized = box_height / H

Horizontal quantities use image width; vertical quantities use image height. Do not divide all coordinates by one dimension or by the largest coordinate in the annotation.

For a worked example, use an image measuring 1280 × 960 pixels and the rectangle (160, 240, 480, 600):

  • Box width: 480 - 160 = 320 pixels.
  • Box height: 600 - 240 = 360 pixels.
  • Horizontal center: (160 + 480) / 2 = 320 pixels.
  • Vertical center: (240 + 600) / 2 = 420 pixels.
  • Normalized horizontal center: 320 / 1280 = 0.25.
  • Normalized vertical center: 420 / 960 = 0.4375.
  • Normalized width: 320 / 1280 = 0.25.
  • Normalized height: 360 / 960 = 0.375.

For class identifier zero, the output line is:

0 0.25000000 0.43750000 0.25000000 0.37500000

These numbers describe annotation geometry, not optical resolution. A camera-resolution calculator does not validate this transformation.

Implement a converter that rejects ambiguous records

  1. Validate each rectangle before producing a label line.

The function below assumes a confirmed single-class interpretation, a validity value of 1, and the image-edge coordinate convention described above. It deliberately rejects unsupported flags rather than silently treating them as objects or background.

import math


def convert_box(row, width, height, class_id):
    if type(width) is not int or type(height) is not int:
        raise ValueError("Image dimensions must be integers")
    if width <= 0 or height <= 0:
        raise ValueError("Image dimensions must be positive")
    if type(class_id) is not int or class_id < 0:
        raise ValueError("Class identifier must be a nonnegative integer")
    if not isinstance(row, list) or len(row) != 5:
        raise ValueError("Expected four corners and one validity flag")

    flag = row[4]
    if type(flag) not in (int, float) or flag != 1:
        raise ValueError("Unsupported validity flag")

    corners = row[:4]
    if any(type(v) not in (int, float) for v in corners):
        raise ValueError("Coordinates must be numeric")
    if not all(math.isfinite(v) for v in corners):
        raise ValueError("Coordinates must be finite")

    x_min, y_min, x_max, y_max = corners
    if not (0 <= x_min < x_max <= width):
        raise ValueError("Invalid horizontal bounds")
    if not (0 <= y_min < y_max <= height):
        raise ValueError("Invalid vertical bounds")

    normalized = (
        (x_min + x_max) / (2 * width),
        (y_min + y_max) / (2 * height),
        (x_max - x_min) / width,
        (y_max - y_min) / height,
    )
    tokens = [f"{value:.8f}" for value in normalized]
    if float(tokens[2]) <= 0 or float(tokens[3]) <= 0:
        raise ValueError("Box became zero-sized after rounding")

    return f"{class_id} " + " ".join(tokens)

Eight decimal places are a serialization choice, not a universal training requirement. Retain enough precision for your image dimensions and smallest accepted rectangles.

Wrap this function with record-level reporting. Include the image identifier, annotation index, and rejection reason in an error log. Mark the image as incomplete when any of its required objects fail conversion; do not quietly publish a partially labeled image.

Do not automatically reverse corners or clip coordinates. Either action can conceal a wrong image association or an earlier transformation error. Apply repairs only under a documented policy, and log the original and repaired values.

Write paired files without losing image identity

  1. Write one label file for each image according to the destination loader's pairing rules.

A common arrangement uses matching stems, such as cell_014.png and cell_014.txt, in corresponding image and label directories. Verify the exact directory convention expected by your loader rather than assuming a particular folder layout.

Write one line for every accepted object. Preserve multiple objects belonging to the same image in the same label file. Confirm that rerunning the converter replaces its generated output instead of appending duplicate rows.

Keep a stable class table alongside the conversion configuration. For example, a two-class table might assign 0 to housing and 1 to connector. Every split must use the same mapping. Validate identifiers against that table; being a nonnegative integer is necessary but not sufficient.

Produce output in a new directory, leaving source annotations unchanged. Publish the generated dataset only after validation passes. Record a conversion version and configuration so the same source can be processed reproducibly.

Reconstruct boxes and inspect overlays

  1. Decode the generated labels back into pixel coordinates and draw them on the exact paired images.

Do not draw directly from the original JSON for this check. That verifies the source display, not the generated training labels.

x_min = W × (x_center_normalized - width_normalized / 2) x_max = W × (x_center_normalized + width_normalized / 2) y_min = H × (y_center_normalized - height_normalized / 2) y_max = H × (y_center_normalized + height_normalized / 2)

The worked example reconstructs 1280 × (0.25 - 0.25 / 2) = 160 pixels for the left edge. Its top edge reconstructs as 960 × (0.4375 - 0.375 / 2) = 240 pixels.

Use fractional coordinates for numerical comparisons. Round only when drawing. With eight-decimal serialization, a maximum corner difference of 0.01 pixel is a reasonable test threshold for this example; choose and document a tolerance appropriate to your own dimensions and precision.

Overlay symptom Likely cause Next check
Boxes are displaced consistently Crop offset or wrong image pairing Compare image identifiers and preprocessing history
Horizontal placement looks correct, vertical placement does not Height normalization error Check denominators and image dimensions
Box size looks correct but position is shifted Corner coordinates treated as centers Inspect the conversion equations
Labels fit one orientation only Rotation applied without transforming annotations Compare stored image orientation with annotation orientation
Geometry is correct but class text is wrong Flag copied as class or inconsistent mapping Compare the class table with source metadata

Inspect small objects, edge-touching objects, multiple-object images, and different image sizes. Random samples alone may miss an entire failure category. The guide to debugging detection output coordinate systems provides related checks when overlays also pass through inference or display transforms.

Audit empty images, counts and dataset splits

  1. Reconcile the source records with the generated dataset before training.

Count input images, source rectangles, accepted rectangles, rejected rectangles, and output label rows. The accepted rectangle count should equal the output row count. Explain every missing image and every rejected object.

A rejected annotation does not prove that an image contains no objects. Keep uncertain images out of the published dataset until reviewed. Only confirmed background images should receive the destination loader's documented empty-label treatment. The guide to background images and negative training examples explains this distinction.

Split data by the unit that could otherwise leak repeated appearances: a physical part, acquisition session, or production batch. Adjacent frames of the same object should not casually land in both training and validation sets.

Before a full run, test a small batch through the actual training loader. Confirm the image count, displayed class names, and reconstructed boxes. A syntactically valid text file can still carry the wrong labels.

Keep acquisition choices separate from annotation conversion

The conversion equations do not depend on a camera model. When building a new collection station, area scan cameras and smart cameras and vision sensors represent different acquisition and inspection arrangements. Use the camera finder to evaluate hardware separately from the label format.

The LS-CMR-0166 is an area scan camera option to evaluate for image acquisition. Confirm that your acquisition software preserves image identity and records the dimensions associated with every saved frame.

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

The LS-SMC-0063 is a smart camera option to evaluate when considering inspection on the device. Before choosing that workflow, verify the available image-export and annotation-transfer functions; this conversion procedure does not establish those capabilities.

Smart cameras & vision sensors1.6 MP smart camera, 60 fps, 16 mm lens, white light (polarized, transparent light) (LS-SMC-0063)LS-SMC-0063
Interface
GigE
Resolution
Up to 3 MP
$1,110.00View part

Neither option removes the need to audit exported images. If capture settings are changing between samples, address exposure, gain and balance consistency before collecting a larger dataset.

Run the final release checklist

Before releasing the converted dataset, confirm:

  • Every annotation resolves to exactly one image with verified dimensions.
  • Coordinate conventions and processing stages match between images and labels.
  • Validity flags are handled separately from class identifiers.
  • Every class identifier exists in the same stable class table.
  • Invalid, nonfinite, reversed, and out-of-bounds rectangles are reported.
  • Generated row counts reconcile with accepted source annotations.
  • Overlays are drawn from generated labels, not just the source JSON.
  • Confirmed background images follow the loader's documented convention.
  • Dataset splits avoid repeated-part and adjacent-frame leakage.
  • A small loader test passes before full training starts.

If your remaining problem is acquisition hardware rather than annotation geometry, start with the camera configuration step. For help matching a capture arrangement to your inspection task, contact the applications team with representative images, object sizes, and collection requirements.

Build the whole station

The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.

Start with the camera

Questions

Can the last number in each rectangle become the class label?

Only when the source documentation explicitly defines that value as the class identifier. A flag indicating that an annotation is valid carries no category information. For a confirmed single-class dataset, assign the configured class separately; for multiple classes, obtain a reliable category mapping before conversion.

Can one image width and height be used for the whole dataset?

Use the dimensions of each actual training image. A shared width and height are safe only after checking that every image has those dimensions and matches its annotations. Cropped, rotated, or padded variants can require different dimensions and coordinate transformations even when they originate from the same camera.

Should a converter automatically clip boxes outside the image?

Not by default. Out-of-bounds coordinates can reveal a wrong image pairing, an incorrect endpoint convention, or missing preprocessing transforms. Investigate first. If clipping is an intentional dataset policy, record each change and reject rectangles that become empty after the repair.

How should images with no target objects be represented?

This depends on the destination loader's documented behavior. Some formats use an empty label file, while other loaders permit missing labels. First verify that the image truly contains no target objects; conversion failures must never be reclassified silently as background examples.

Why can training fail even when every normalized number is valid?

Check the image-to-label association, class mapping, and processing history. A box can be mathematically valid yet belong to another image or describe coordinates before a crop or rotation. Reconstruct overlays from the generated files and test a small batch through the actual training loader.