This script takes two scene linear images of a color chart and derives a best-fit RGB matrix that makes the "Source" image as similar to the "Target" image as possible.
Install the needed libraries with pip install -r requirements.txt
and run with python camera_matcher.py
and follow the instructions.
Given an RGB (3-dimensional) color patch x
from the Source image and a RGB color patch y
from the Target image, we suppose that y ~ A(x + b)
, where b
is either 1-dimensional or 3-dimensional, and A
is a 3x3 matrix. The b
term exists to handle any flare or reflections that was introduced or removed between the capture of the two images. We use gradient descent to identify the parameters A
and b
.
The Error modelling pipeline looks like this, starting with the code values extracted from the source image.
- Source image code values for each patch
- Exposure adjustment, which is a global gain that's automatically applied to minimize error relative to the reference chart
- White balance coefficients (set to
$(1, 1, 1)$ if--skip-auto-wb
is set) - Fitted 3x3 matrix (shared across multiple source images) which is intended to go from the code values resulting from the previous step to the target gamut. This matrix is fitted to minimize error resulting from the end of this pipeline. There are three possibilities depending on what
--matrix-constraint
is set to: If set torows-sum-to-one
, then each row will sum to 1.0 and$(1,1,1)$ in will be mapped to$(1,1,1)$ out. If set torow-two-sums-to-one
, then just the second row will be normalized to sum to 1.0 (useful for matrices to XYZ). If set tomatrix-sums-to-three
, then we will only ensure that the sum of all 9 entries in the matrix is 3.0 - 3x3 matrix to go from the target gamut to XYZ (not fitted)
- CAT02 chromatic adaptation matrix to go from the target gamut's white point to the white point of the reference chart (skipped if
--skip-chromatic-adaptation
is set). - The resulting XYZ values are converted to LAB, under the reference chart's specified lighting conditions.
- Delta-E is computed in LAB space, as
$\sqrt{(L_{src} - L_{ref})^2 + (A_{src} - A_{ref})^2 + (B_{src} - B_{ref})^2}$ . The mean Delta-E across all patches is to be minimized via the exposure, white balance, and 3x3 matrix.
- Run linter with
mypy *.py src tests
- Run unit tests with
pytest --cov tests
- Reformat code with
black .