Skip to content

Color calibration: when to use it, what it does, and the white-balance / vignetting knobs

  • Status: unverified
  • Applies to: Metashape Pro 2.x and Standard 2.x — same Calibrate Colors command since PhotoScan 1.4 (Jan 2018)
  • Edition: Pro / Standard
  • Diátaxis: explanation
  • Confidence: high
  • Last reviewed: 2026-10-06

Confidence: high. The feature description is from the official manual (chapters on Build Texture and Build Orthomosaic). Operational nuances (white-balance vs per-band, the reset-button bug in 1.5.3-1.5.4, accessing vignetting coefficients) are forum-attested with permalinks. The Chunk.image_brightness, Chunk.image_contrast, and Chunk.calibrateColors API are introspection-confirmed. The section The correction model, measured was measured on Metashape Pro 2.3.2 (macOS) with a contributor-private dataset of fixed cameras, 4096 × 1728 8-bit PNG. You will not reproduce the exact numbers without your own data.

Scope statement. The official manual extensively documents Calibrate Colors and the Color calibration dialog parameters at the GUI feature-reference level. This article picks up where the manual stops: what each parameter means operationally; the white-balance vs per-band distinction's effect on multispectral / NDVI workflows; the Python API equivalent; the historical context (replacement for the 1.3-era Color Correction checkbox); and four common failure modes with fixes.

Problem

Your project's source images have inconsistent brightness or white balance. Common causes:

  • Long capture sessions where lighting drifted (sun moved across the sky during a 3-hour drone flight; cloud cover changed mid-flight).
  • Mixed cameras with different built-in white-balance algorithms.
  • Vignetting — uncorrected lens-darkening at the corners of each image, producing visible pattern in the orthomosaic that follows the camera-flight line.
  • Overlapping images from different exposures (HDR bracketed, automatic exposure changing between captures).

The visible symptom: orthomosaic seams appear as bright / dark stripes where adjacent images had different exposure. Texture-on-mesh shows similar checkerboard pattern from per-image colour shifts. Both are aesthetically wrong even when the geometric reconstruction is fine.

Metashape's Tools → Calibrate Colors… command (introduced in PhotoScan 1.4, January 2018) addresses all three: brightness evening, white-balance evening, and vignetting correction. This article covers what each option does, when to use it, and the operational pitfalls.

What Calibrate Colors does

The procedure analyses overlapping image regions across the chunk's photo set and estimates per-image brightness and vignetting coefficients that minimise the colour difference between observations of the same surface point from different cameras.

"If the lighting conditions have been changing significantly during capturing scenario, it is recommended to use Calibrate colors option from the Tools menu before Build texture procedure. The option can help to even brightness and white balance of the images over the data set." — Metashape Pro Manual 2.3, § Color calibration

The corrections are stored on each camera and applied automatically during Build Texture and Build Orthomosaic. You do NOT see modified pixels in the Photos view; the correction is a multiplicative adjustment applied at texture-fetch time.

The key historical context (from the source thread):

"Yes, it can be said that Calibrate Colors procedure substitutes Color Correction option in Build Texture / Build Orthomosaic dialogs. This new function can be used any time, before texture/orthomosaic are generated." — Agisoft support, 2018-01-22, PhotoScan 1.4 (permalink, archived 2026-09-24)

In PhotoScan 1.3 and earlier, the equivalent functionality was a checkbox inside Build Texture / Build Orthomosaic called Color Correction. The 1.4 redesign separated it into its own command so that:

  1. The same calibration could feed multiple downstream products (one Calibrate Colors run, then both Build Texture and Build Orthomosaic use the result).
  2. The calibration could be inspected / reset between texture builds without rebuilding from scratch.
  3. The procedure became scriptable as chunk.calibrateColors(...).

The two main options

The Calibrate Colors dialog has two key parameters that are often misunderstood:

Source data

Defines what overlapping-region geometry the procedure uses to find shared surface points across images:

Source data Quality Speed When to use
Tie points rough fast Initial pass; very large datasets where mesh isn't built
Model (mesh) best slow (mesh must be built) Most accurate; recommended for texture quality
DEM medium medium Aerial datasets where a single mesh isn't feasible

The manual is explicit:

"Model — gives more precise results, but only on condition that the surface is detailed enough. This parameter value is the recommended one if the aim is to calibrate colors to improve the quality of the model texture." — Metashape Pro Manual 2.3

For aerial projects: build the DEM first, then Calibrate Colors → Source data: DEM. Skip the Model option since the single-block mesh is often impractical at large project sizes. For close-range / object scans: build the mesh first, then use Model as source.

Calibrate white balance

A checkbox controlling whether each colour band is corrected independently (separately) or together:

Setting Behaviour When to use
Unchecked All bands corrected with the same multiplier per image Brightness-only correction; preserves per-band relationships
Checked Each band gets its own per-image multiplier Different cameras' white-balance shifts; mixed lighting

"White balance calibration options means that each band would be corrected separately, whereas with the option unchecked all bands will be corrected simultaneously." — Agisoft support, 2018-01-22, PhotoScan 1.4 (permalink, archived 2026-09-24)

For multispectral / NDVI / scientific work: leave unchecked to preserve the per-band relationships (NDVI = (NIR-RED)/(NIR+RED) depends on consistent inter-band ratios). For visual / aesthetic work: check it for the cleanest result.

Recipe — Python invocation

Demo verified: ✗ — pending Tier 3 reproduction on a real Metashape install.

import Metashape

chunk = Metashape.app.document.chunk

# Build mesh first if you'll use Model as source
if not chunk.model:
    chunk.buildDepthMaps(downscale=2,
                         filter_mode=Metashape.FilterMode.MildFiltering)
    chunk.buildModel(surface_type=Metashape.SurfaceType.Arbitrary,
                     source_data=Metashape.DataSource.DepthMapsData)

# Run color calibration
chunk.calibrateColors(
    source_data=Metashape.DataSource.ModelData,   # or TiePointsData / ElevationData
    white_balance=True,                           # the "Calibrate white balance" checkbox
)

# Now build texture / orthomosaic — corrections apply automatically
chunk.buildUV()
chunk.buildTexture(blending_mode=Metashape.BlendingMode.MosaicBlending)

The source_data= parameter accepts the same DataSource enum values as other build operations. The white-balance keyword is white_balance; Metashape 2.3.2 rejects the older color_balance with NameError: Invalid argument name. The task attribute was renamed from calibrate_color_balance to white_balance in 1.6.0 (Metashape Python API Reference, version 2.3.2, ch. 3 "Python API Change Log" → "Metashape version 1.6.0").

Per-image manual brightness adjustments

For finer control, Tools → Adjust Color Levels… (per-image, right-click in the Photos pane) lets you set per-image brightness / contrast manually. The settings are stored on each camera and apply during build operations.

Demo verified: ✗ — pending Tier 3 reproduction on a real Metashape install.

import Metashape

chunk = Metashape.app.document.chunk

# Set chunk-wide brightness/contrast
chunk.image_brightness = 500   # range 0-1000; 500 is "no change"
chunk.image_contrast = 200     # range 0-1000; 200 is "no change"

The forum-attested pattern from Agisoft support:

"You can set brightness and contrast in the following way: chunk.image_brightness = 500 / chunk.image_contrast = 200. But there's no straightforward way to estimate the default values, like via GUI." — Agisoft support, 2019-07-23, Metashape 1.5 (permalink, archived 2026-09-23)

The "no straightforward way to estimate default values" caveat is important: the GUI's Adjust Color Levels dialog runs an analysis to suggest defaults, but the analysis isn't exposed to scripts. For batch / automated workflows, you must hardcode reasonable values (often 500 / 200 works).

Vignetting correction

If your camera writes vignetting metadata (some calibrated DSLR / industrial cameras do), Metashape automatically loads and applies it at Add Photos time:

"Vignetting correction information is saved in meta data in these types of cameras. This information will be loaded automatically into Metashape and can be used to improve orthomosaic generation results." — Metashape Pro Manual 2.3, § Vignetting correction

For cameras without metadata-encoded vignetting, Calibrate Colors estimates vignetting coefficients along with the brightness/white-balance corrections. They are stored as two separate polynomials, one on the sensor and one on each camera; see The correction model, measured below.

Reading the estimated vignetting coefficients

The vignetting coefficients aren't directly accessible through the Python API in some Metashape versions. The forum-attested workaround:

"You can try to open doc.xml inside the corresponding to chunk.zip archive in project.files directory and check for section contents." — Agisoft support, 2020-02-12, Metashape 1.6 (permalink, archived 2026-09-23)

The project.files/<chunk_id>/0/chunk.zip archive contains doc.xml with <vignetting> blocks; each block contains a 2D polynomial coefficient grid, indexed [i, j] with i+j ≤ 3.

The coefficients are also readable from Python, by indexing rather than by attribute. Camera.vignetting, Sensor.vignetting and the Vignetting class were added in Metashape 1.5.0, together with Chunk.calibrateColors() (Metashape Python API Reference, version 2.3.2, ch. 3 "Python API Change Log" → "Metashape version 1.5.0"). The indexing below was checked on 2.3.2 only. dir() on a Metashape.Vignetting object lists only copy, but camera.vignetting[0][i, j] and camera.sensor.vignetting[0][i, j] return the coefficients, one Vignetting per band. Both lists are also assignable.

The polynomial form is documented, in the reflectance section of the Pro manual rather than the color-calibration one:

"Vignetting is modeled in Metashape using a 3 degree bivariate polynomial: V(x, y) = exp(sumij cij * xi * yj) […] To compensate vignetting in the image, each pixel value is divided by corresponding vignetting factor." — Metashape Pro User Manual, ch. 5 "Measurements" § "Vegetation indices calculation" → "Vignetting correction" (Pro 2.3, p. 129)

The manual normalises the coordinates to x = 2(i + 0.5)/w − 1 and y = 2(j + 0.5)/h − 1, so the image spans [−1, 1] with (−1, −1) at the top-left corner.

For most users, the calibration is opaque and "just works"; the internal-XML inspection is only needed for forensic debugging of mis-corrected images.

The correction model, measured

Measured on Metashape Pro 2.3.2 with the contributor-private dataset described in the preamble. The project was aligned with one sensor per camera, then calibrateColors(source_data=TiePointsData, white_balance=False) was run, and corrected copies were written with chunk.convertImages(color_correction=True) to lossless TIFF. Each finding compares those copies with the originals pixel by pixel.

Two polynomials: falloff on the sensor, gain on the camera

Calibrate Colors writes two <vignetting> blocks, with different terms:

Stored on Non-zero terms What it is
sensor.vignetting (2,0), (0,2), (1,1); (0,0) is 0 Spatial falloff of the lens
camera.vignetting (0,0), (1,0), (0,1) Per-image level: a gain exp(c₀₀) and a linear tilt across the frame

The split does not depend on how sensors are shared. With one sensor per camera, every sensor and every camera carried a non-zero block, so each camera has its own falloff and its own gain. With one sensor shared by several cameras, the falloff block is shared and the camera blocks still differ per image.

There is no option to fit only the gain. calibrateColors takes source_data, white_balance and cameras, and Metashape.Tasks.CalibrateColors().encode() returns no further parameters.

Both blocks are applied together, on the stored pixel values

The manual's formula holds with the two blocks summed in one exponent:

I' = I / exp( Σ cᵢⱼ xⁱ yʲ )      camera terms + sensor terms

What the measurement adds is that I is the stored, gamma- encoded 8-bit value, not a linearised one:

  • Falloff. With the camera block zeroed, dividing each stored pixel by V(x, y) rebuilt from the sensor coefficients reproduces the corrected image to 0.58 levels RMS. Doing the division in linear light (sRGB-decode, divide, re-encode) leaves 1.57 levels.
  • Gain. With the sensor block zeroed and the camera block reduced to (0,0), the corrected image is the original times exp(−c₀₀) to 0.57 levels RMS, with no free parameter; a fitted scalar matches exp(−c₀₀) to within 0.7% on every camera.

The 8-bit rounding of the output alone accounts for about 0.3 levels.

convertImages uses the coefficients as they are

Coefficients edited after Calibrate Colors are honoured, not recomputed. That makes it possible to keep only the per-image gain:

Demo verified: ✗ — not run on an Agisoft sample dataset. Run end-to-end on Metashape Pro 2.3.2 with the contributor-private dataset above; the table below is from that run.

import Metashape

chunk = Metashape.app.document.chunk
chunk.calibrateColors(source_data=Metashape.DataSource.TiePointsData,
                      white_balance=False)

for camera in chunk.cameras:
    sensor = camera.sensor
    if sensor is not None and sensor.vignetting:
        # drop the lens falloff
        sensor.vignetting = [Metashape.Vignetting() for _ in sensor.vignetting]
    if camera.vignetting:
        # keep only the per-image gain exp(c00), dropping the tilt
        bands = []
        for v in camera.vignetting:
            g = Metashape.Vignetting()
            g[0, 0] = v[0, 0]
            bands.append(g)
        camera.vignetting = bands

chunk.convertImages(path="corrected/{filename}.tif", color_correction=True)

Fitting a single scalar per image and channel to the result:

Coefficients kept Residual of a scalar fit
All, as calibrated 3.27 levels
Sensor falloff zeroed 2.17 levels
Gain (0,0) only 0.39 levels

A residual this close to the rounding floor means the gain-only output is a pure per-image multiply. Assigning to sensor.vignetting leaves camera.vignetting unchanged.

Is a gain in gamma space physically wrong?

Exposure and lens falloff act on scene radiance, so a correction belongs in linear light. Applying it to encoded values is still a correction of the right form, provided the encoding is a pure power law. If v = L^(1/γ), then g · v = (g^γ · L)^(1/γ): multiplying the encoded value by g multiplies linear radiance by g^γ. A smooth multiplicative field in encoded space is likewise a smooth multiplicative field in linear space.

Only the meaning of the coefficients changes. They describe the correction in encoded units, and the linear-light factor is exp(Σ cᵢⱼ xⁱ yʲ)^γ. Because Calibrate Colors fits them to encoded intensities, the exponent is absorbed into the fit.

This is also why a free-gain fit cannot tell the two spaces apart: on the same images, a scalar fitted in encoded space and one fitted in linear light left 3.27 and 3.30 levels. Only holding the field at the stored coefficients, as in the falloff measurement above, separates them.

Where the equivalence breaks:

  • The sRGB toe. Below about 10/255, sRGB is linear rather than a power law, so dark pixels get g in linear light while mid-tones get about g^2.2. At 8 bits and gains within ±10%, the error is under a level.
  • Camera tone curves that are not a power law: BT.709, highlight knees, S-curves, log profiles. A scalar in encoded space cannot represent a gain under such a curve, and the error grows toward the highlights. The measurement above assumed sRGB; the cameras' actual transfer curve was not checked.
  • Black-level offsets and clipped pixels break any purely multiplicative model, in either space.
  • Reusing the coefficients in a linear pipeline. Applied unchanged to linear data, they correct by roughly a factor of γ too little in log terms. Raise the factor to γ first.

When to NOT use Calibrate Colors

Scenario Why skip
Single-flight datasets with stable lighting No correction needed; the procedure burns time without improving the result
Multispectral / scientific NDVI workflows where exact per-band radiometry matters Calibration may introduce artefacts that confound NDVI calculation; use radiometric calibration (reflectance panels) instead
Already-calibrated reflectance imagery (MicaSense, Parrot Sequoia post-reflectance-cal) The reflectance calibration is the radiometric truth; further calibration distorts it
Very large datasets (>10,000 images) on time-constrained pipelines Processing time is significant; if texture quality is acceptable without it, skip

For multispectral / reflectance workflows, see Multispectral imaging: per-file band declaration and master-band change.

Common failure modes

"Calibrate Colors made my orthomosaic worse"

Possible causes:

  • Wrong Source data choice for the dataset. Tie-points source on a sparse-feature scene gives bad estimates. Switch to Model or DEM.
  • Insufficient overlap between images. The procedure needs overlapping regions to compare. Below ~50% overlap, estimates are noisy.
  • Sky / water in the images dominating the overlap regions. Mask sky / water before calibration; see Automatic sky masking.
  • Mixed sensor types (RGB + thermal in same chunk). The procedure assumes consistent radiometry across the source set. Calibrate per-modality, not per-chunk.

"Reset Calibrate Colors didn't undo the correction"

In Metashape 1.5.3-1.5.4, the Reset Color Calibration button had a known bug:

"I think the reset color calibration isn't working for me. I'm using 1.5.3 build 8469 on mac. When I try the reset button, it might change 1 or 2 thumbnails back to the original appearance, but still has the 'corrected' version if I open the individual image. I managed to remove it by editing out the blocks in the chunk. and frame. files for that model, but the button would be easier!" — hairyfreak, 2019-10-02, Metashape 1.5.3 (permalink, archived 2026-09-23)

The bug was fixed in Metashape 1.5.5 per the same thread. If you're on 1.5.3 or 1.5.4, manually delete the <vignetting> blocks from the chunk's internal XML; if you're on 1.5.5+ or 2.x, the GUI button works.

"Calibrate Colors is too slow"

The procedure is O(images × overlap-regions). On a 5,000-image project with high overlap, expect 1-3 hours.

To speed up:

  • Use Tie points as source (fastest; lower quality but often good enough for orthomosaic).
  • Disable Calibrate white balance if brightness-only is sufficient.
  • Mask sky / water (smaller search regions).
  • For very large datasets: split into chunks, calibrate each, then merge.

Caveats

  • Calibrate Colors must run BEFORE Build Texture / Build Orthomosaic to take effect. Running it after produces the calibrated values in the chunk's per-camera storage, but the existing texture / orthomosaic are still based on the un-calibrated values until rebuilt.
  • The corrections are NOT applied to the source images (the photo files on disk are unchanged). They apply at Metashape's internal texture-fetch step. To get corrected copies on disk, use chunk.convertImages(color_correction=True) (Tools → Convert Images). Chunk.convertImages() was added in Metashape 2.1.2; earlier versions have a Metashape.Tasks.ConvertImages task class (named Tasks.UndistortPhotos before 1.6.0), whose color-correction support was not checked. The older route is the Undistort Photos workflow with calibration applied.
  • The Reset button (in 2.x) clears the correction without modifying the source images. Re-running Calibrate Colors recomputes the correction.
  • Per-camera image_brightness / image_contrast chunk attributes are global to the chunk, not per-image. For per-image adjustments, use the GUI's Adjust Color Levels… on individual cameras.
  • Some cameras embed bad vignetting metadata. Particularly for older smartphones and budget action cameras, the EXIF-encoded vignetting can be wrong, producing visible artefacts. Run Calibrate Colors (estimated, not metadata-encoded) as a sanity check.
  • For thermal imagery, do NOT calibrate. Thermal pixel values are radiometric (object temperature). Calibration treats them as visual brightness, distorting the measurement. Process thermal in a separate chunk and reference it back via Transferring camera orientation between modalities (RGB → thermal).

See also

References