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 Colorscommand 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, andChunk.calibrateColorsAPI 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:
- The same calibration could feed multiple downstream products (one Calibrate Colors run, then both Build Texture and Build Orthomosaic use the result).
- The calibration could be inspected / reset between texture builds without rebuilding from scratch.
- 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:
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 timesexp(−c₀₀)to 0.57 levels RMS, with no free parameter; a fitted scalar matchesexp(−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
gin linear light while mid-tones get aboutg^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 aMetashape.Tasks.ConvertImagestask class (namedTasks.UndistortPhotosbefore 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_contrastchunk 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¶
- Texture blending modes — what each one actually does — the downstream consumer of the calibration; certain blending modes (Mosaic) interact with calibration in visible ways.
- Orthomosaic export — the 4GB / BigTIFF limit — the orthomosaic that consumes the calibration.
- Multispectral imaging: per-file band declaration and master-band change — for the reflectance-calibration alternative to visual-only colour calibration.
- Automatic sky masking with ONNX — pre-clean for sky / water regions that confuse Calibrate Colors.
References¶
- Metashape Pro User Manual (2.3), ch. 4 Building model texture → Color calibration and ch. 5 Build Orthomosaic → Color calibration.
- Metashape Python API Reference (2.3.1):
Chunk.calibrateColors,Chunk.image_brightness,Chunk.image_contrast,DataSourceenum. - Forum thread, Questions about new calibrate color feature in 1.4, 2018 (archived 2026-09-24) — feature introduction and the white-balance-checkbox semantic.
- Forum thread, How to get the Vignetting coefficients?, 2020 (archived 2026-09-23) — the doc.xml inspection workaround.
- Forum thread, reset calibrate color not working, 2019 (archived 2026-09-23) — the 1.5.3-1.5.4 reset-button bug; fixed in 1.5.5.
- Forum thread, Run "Set Brightness" tool to estimate brightness from python, 2019 (archived 2026-09-23) — the per-chunk image_brightness / image_contrast attributes.