System designHow-to

Export camera intrinsics to JSON for vision workflows

Export camera intrinsics to JSON with a clear schema, preserved distortion conventions, image dimensions, and reload checks for reliable interchange.

LumaSpec Vision8 min read Share on LinkedIn

On this page 9 sections
  1. 01Agree on the calibration contract first
  2. 02Step 1: Extract values without changing their meaning
  3. 03Step 2: Define an explicit JSON structure
  4. 04Step 3: Serialize, reload, and check the structure
  5. 05Step 4: Verify geometry in the receiving application
  6. 06Step 5: Track image transformations explicitly
  7. 07Step 6: Check acquisition and deployment compatibility
  8. 08Acceptance checklist and next step
  9. 09Questions

A calibration sample may display focal lengths and distortion values without providing a portable export. The missing piece is usually a data contract: which values the receiving application expects, how they are arranged, and which image coordinates they describe.

This workflow is for applications exchanging an existing camera calibration between acquisition and processing software. You will define a small JSON structure, preserve its numerical values, reload it, and check that the receiving application interprets it correctly. The example uses a conventional pinhole model with five distortion coefficients; other models need their own explicit definitions.

Agree on the calibration contract first

JSON can store arrays and numbers, but it does not define camera geometry. Two applications can read the same file successfully while interpreting its contents differently.

Before exporting, establish whether the destination accepts a documented JSON schema. If it does, follow that schema rather than inventing another one. If it does not, agree on a structure and write an adapter that translates it into the destination's native calibration object.

Keep serialization separate from model conversion. Writing a focal length into JSON does not convert it from physical units to pixels. Renaming a distortion field does not change the mathematical model behind it.

For retaining the original calibration and its acquisition context, follow saving camera calibration parameters for reliable reuse. The workflow below addresses the interchange file and its validation, not the calibration procedure itself.

Item What the file must establish Common failure
Intrinsic matrix Shape, layout, and pixel units Flattened values reconstructed in the wrong order
Distortion Model, mapping direction, and coefficient order Similar-looking vectors treated as interchangeable
Image dimensions Width and height of the calibrated image Full-frame calibration applied to a cropped image
Pixel coordinates Origin and axis directions Half-pixel or axis-direction disagreement
Image state Raw distorted or already rectified Distortion correction applied twice
Schema version Meaning of each field Reader silently accepting incompatible changes

Step 1: Extract values without changing their meaning

  1. Read the calibration through the source application's documented interface, then identify every parameter before mapping it into JSON.

For the example model, the intrinsic matrix contains horizontal and vertical focal lengths, the principal point, and zero skew. The focal lengths and principal-point coordinates are expressed in pixels.

K = [[fx, 0, cx], [ 0, fy, cy], [ 0, 0, 1]] x = X / Z y = Y / Z u = fx × xd + cx v = fy × yd + cy

Here, X, Y, and Z are camera-frame coordinates. The distortion model transforms normalized ideal coordinates x and y into distorted coordinates xd and yd. The matrix then maps those distorted coordinates into image pixels.

Do not assume a source parameter vector contains this matrix directly. It may contain physical focal length, pixel pitch, a different distortion model, or additional parameters. Use documented conversions and preserve the native calibration alongside the export.

Record the image width and height used during calibration. Also identify any crop, binning, resize, rotation, or rectification applied before the receiving application sees an image.

Step 2: Define an explicit JSON structure

  1. Create a versioned structure that carries the matrix and its interpretation together.

The following is an illustrative interchange contract, not a universal standard or a factory calibration. Its numbers describe a hypothetical image. Replace them with values extracted from your own calibration through a documented mapping.

{
  "schema_version": "1.0",
  "calibration_id": "inspection-camera-full-frame",
  "image": {
    "width_px": 1280,
    "height_px": 1024
  },
  "image_state": "raw_distorted",
  "pixel_coordinates": {
    "origin": "top_left_pixel_center",
    "x_axis": "right",
    "y_axis": "down"
  },
  "camera_axes": {
    "x": "right",
    "y": "down",
    "z": "forward"
  },
  "intrinsics": {
    "matrix_layout": "row_major",
    "K": [
      [1200.0, 0.0, 639.5],
      [0.0, 1190.0, 511.5],
      [0.0, 0.0, 1.0]
    ]
  },
  "distortion": {
    "model": "brown_conrady_5",
    "mapping": "ideal_to_distorted",
    "coordinate_space": "normalized_camera",
    "coefficient_order": ["k1", "k2", "p1", "p2", "k3"],
    "coefficients": [-0.08, 0.01, 0.0005, -0.0003, 0.0]
  }
}

In this contract, the top-left pixel center has coordinates (0, 0). Matrix rows are stored as nested arrays, not as a string containing formatted output.

The distortion label is defined by these equations:

r² = x² + y² radial = 1 + k1 × r² + k2 × r⁴ + k3 × r⁶ xd = x × radial + 2 × p1 × x × y + p2 × (r² + 2 × x²) yd = y × radial + p1 × (r² + 2 × y²) + 2 × p2 × x × y

Store coefficient order even when the model name appears familiar. Different interfaces may expose similar parameters in different arrangements. Models with additional radial terms, fisheye projection, or division distortion require different contracts.

If the source calibration includes stereo relationships, keep those separate from this single-camera intrinsic structure. Importing stereo calibration parameters across software formats addresses the additional transform and convention checks.

Step 3: Serialize, reload, and check the structure

  1. Export numerical values with a JSON library, reject non-finite numbers, and reload the completed file.

Avoid building JSON through string concatenation or copying rounded values from a display window. Preserve the source's available numerical precision instead of choosing a fixed number of decimal places for appearance.

The Python example below validates the narrow contract shown above. Save the illustrative object as candidate.json to exercise it. In a production exporter, construct candidate directly from the documented source calibration API instead.

import json
import math
from pathlib import Path


def require(condition, message):
    if not condition:
        raise ValueError(message)


def finite_numbers(values):
    return all(
        type(value) in (int, float) and math.isfinite(value)
        for value in values
    )


def validate(c):
    require(c["schema_version"] == "1.0", "Unsupported schema")
    require(c["image_state"] == "raw_distorted", "Wrong image state")
    require(c["pixel_coordinates"] == {
        "origin": "top_left_pixel_center",
        "x_axis": "right",
        "y_axis": "down"
    }, "Unsupported pixel coordinates")
    require(c["camera_axes"] == {
        "x": "right", "y": "down", "z": "forward"
    }, "Unsupported camera axes")

    for key in ("width_px", "height_px"):
        value = c["image"][key]
        require(type(value) is int and value > 0, "Invalid image size")

    intrinsics = c["intrinsics"]
    require(intrinsics["matrix_layout"] == "row_major", "Wrong layout")
    K = intrinsics["K"]
    require(isinstance(K, list) and len(K) == 3, "Wrong matrix shape")
    require(all(isinstance(row, list) and len(row) == 3 for row in K),
            "Wrong matrix rows")
    require(finite_numbers([v for row in K for v in row]),
            "Invalid matrix number")
    require(K[0][0] > 0 and K[1][1] > 0, "Invalid focal length")
    require(K[0][1] == 0 and K[1][0] == 0, "Unsupported skew")
    require(K[2] == [0, 0, 1], "Invalid matrix bottom row")

    d = c["distortion"]
    require(d["model"] == "brown_conrady_5", "Unsupported model")
    require(d["mapping"] == "ideal_to_distorted", "Wrong mapping")
    require(d["coordinate_space"] == "normalized_camera",
            "Wrong distortion coordinates")
    require(d["coefficient_order"] == ["k1", "k2", "p1", "p2", "k3"],
            "Wrong coefficient order")
    values = d["coefficients"]
    require(isinstance(values, list) and len(values) == 5,
            "Wrong coefficient count")
    require(finite_numbers(values), "Invalid distortion number")


candidate = json.loads(Path("candidate.json").read_text(encoding="utf-8"))
validate(candidate)

path = Path("camera_intrinsics.json")
path.write_text(
    json.dumps(candidate, indent=2, allow_nan=False),
    encoding="utf-8"
)

reloaded = json.loads(path.read_text(encoding="utf-8"))
validate(reloaded)
require(reloaded == candidate, "Round-trip values changed")
print("JSON structure and round-trip values passed")

This check rejects unsupported conventions rather than guessing how to convert them. Missing fields also stop execution. A production application should report these failures clearly and leave the last accepted calibration active, rather than substituting zero values.

Numerical equality after reloading proves preservation within this parser's representation. It does not prove that another application interprets the file correctly.

Step 4: Verify geometry in the receiving application

  1. Load the exported file through the destination adapter and compare projected test points against the source implementation.

Use the same camera-frame points in both implementations. Include the optical axis, off-axis points in each image quadrant, and points near the usable image boundary. Avoid Z = 0 and points behind the camera.

For the illustrative calibration, the optical axis projects to (639.5, 511.5) pixels. This checks principal-point placement but cannot expose a distortion-order mistake, because distortion vanishes at the normalized origin.

An off-axis example uses x = 0.1 and y = 0.05:

r² = 0.1² + 0.05² = 0.0125 radial = 1 - 0.08 × 0.0125 + 0.01 × 0.0125² = 0.9990015625 xd = 0.09989540625 yd = 0.049955828125 u = 1200 × xd + 639.5 ≈ 759.37449 pixels v = 1190 × yd + 511.5 ≈ 570.94744 pixels

Store several expected projections as a separate validation fixture. Set the acceptance tolerance according to numerical precision and any deliberate model conversion. For an unchanged double-precision implementation, a starting numerical check might use 0.000001 pixel, but that is not a camera accuracy specification.

If values match yet projections differ, investigate matrix layout, coefficient order, units, and distortion direction. Comparing camera calibration parameters across workflows provides the broader comparison process.

Finally, inspect a real calibration image through the destination pipeline. Numerical fixtures catch interpretation errors; image checks also expose incorrect image selection or preprocessing.

Step 5: Track image transformations explicitly

  1. Reject dimension mismatches unless the application applies a documented transformation to the calibration.

Suppose the original image is 1280 × 1024 pixels. Cropping 100 pixels from the left and 40 pixels from the top shifts the principal point:

cx_crop = 639.5 - 100 = 539.5 pixels cy_crop = 511.5 - 40 = 471.5 pixels

For a crop without resampling, focal lengths and normalized distortion coefficients remain unchanged. Record the cropped dimensions and retain the relationship to the original calibration.

Resizing also requires a precise pixel-coordinate mapping. For a half-size resize using the mapping u' = 0.5 × (u + 0.5) - 0.5, the example becomes:

fx' = 0.5 × 1200 = 600 pixels fy' = 0.5 × 1190 = 595 pixels cx' = 0.5 × (639.5 + 0.5) - 0.5 = 319.5 pixels cy' = 0.5 × (511.5 + 0.5) - 0.5 = 255.5 pixels

A resize implementation using another mapping requires the corresponding transformation. Do not apply a half-pixel correction automatically to every library.

Rectified images require the rectification output's camera model, not an unqualified copy of the raw calibration. See offline image rectification from stored calibration data for that processing boundary. If the destination estimates pose, choosing intrinsics for rectified-image pose estimation explains which matrix belongs with those images.

Step 6: Check acquisition and deployment compatibility

  1. Confirm that the deployed acquisition path produces the image described by the file, then test loading it after an application restart.

For existing hardware, first compare actual frame dimensions and processing settings with the export. Camera interface compatibility does not establish calibration compatibility.

If you are selecting area scan cameras, use the camera finder to review acquisition requirements. For a GigE acquisition path, the LS-CMR-0053 is a catalog option. Its interface does not define a JSON calibration schema.

Area scan cameras12 MP GigE area scan camera, IMX226, 1/1.7" rolling shutter, 9 fps (LS-CMR-0053)LS-CMR-0053
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
12 MP
$560.00View partBuild a system with it

For a USB3 acquisition path, review the LS-CMR-0166. Treat the calibration export as a separate software integration task rather than assuming it follows from camera selection.

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

When processing runs on vision controllers, verify file permissions, configuration paths, and the destination parser's numerical behavior. Record which calibration identifier was accepted at startup. Do not silently select an unrelated file after a loading failure.

For live deployment, write and validate a temporary file before replacing the active configuration. Follow the operating system's documented replacement behavior and keep a recoverable previous version.

Acceptance checklist and next step

Before releasing the exporter, check the following:

  • The destination accepts the documented schema version and rejects unsupported versions.
  • Matrix entries come from the source calibration, not rounded display text.
  • Distortion order, direction, and coordinate space are explicit.
  • Image dimensions and pixel-coordinate conventions match the actual frames.
  • Reloading preserves all required values without non-finite numbers or missing fields.
  • Independent source and destination projections agree at multiple off-axis points.
  • Cropped, resized, and rectified images cannot accidentally use an incompatible calibration.
  • Restart testing confirms that the intended file is loaded and its identifier is recorded.

Do not use this example contract unchanged for a model it cannot represent. Preserve that model's parameters and implement a supported destination adapter instead.

For a new acquisition system, start with the camera configuration step. For help matching acquisition hardware to an existing calibration workflow, share the required schema, frame dimensions, and processing sequence through applications support.

Questions

Can I export only the intrinsic matrix?

Only when the receiving application explicitly accepts that representation and obtains the remaining information elsewhere. A matrix alone usually leaves distortion, calibrated image dimensions, and coordinate conventions unspecified. Include those fields together, or reference an equally explicit configuration contract that the destination validates.

How many decimal places should the JSON file preserve?

Preserve the precision available through the source calibration interface rather than copying rounded display values. Use a numerical JSON serializer and test the destination parser. Extra decimal digits cannot improve the calibration itself, but premature rounding can introduce avoidable differences between implementations.

Can distortion coefficients be copied between any two applications?

No. Different models can use the same coefficient count while describing different equations, units, or mapping directions. Establish the source model and the destination model explicitly. If they differ, use a documented conversion or retain the native model instead of merely rearranging values.

Does changing the camera lens require a new JSON file?

Usually, replacing the lens or changing the optical setup requires calibration review and often recalibration. Also review focus changes, sensor readout changes, and image preprocessing. A JSON export preserves existing parameters; it does not establish that those parameters still describe the current imaging system.