Colour Gamut Expansion

Making HD content use the full colour range of a UHD screen. Research internship, Imaging Systems Lab, Changwon National University, South Korea, 2018.

Your TV is 4K/UHD and can show almost twice the colour range of HD, but most of what you watch is still mastered in HD, so the extra range sits unused. Gamut expansion pushes each pixel’s colour outward so the content fills the screen, keeping its hue and brightness. I implemented a published method, found why it barely changes real images, and fixed it with one extra parameter, β.

+45% saturation gain on a portrait, vs +6% for the published method at the same α
13% → 31% of the UHD colour gamut the image actually uses
α 0–0.9 whole range now useful; the published method does nothing above α ≈ 0.6

Try it

Pick an image, then move α and β. Drag the divider on the image to compare. Everything runs in your browser; uploaded photos never leave your device.

Colours inside the dashed red gamut stay as they are. Lower α expands more.

β = 0 is the published method. Higher β fits the reference gamut (cyan) to the image.

HD original Expanded
pixels before pixels after UHD (BT.2020) HD (BT.709) β·HD inner gamut
Pixels expanded
–
Mean saturation gain
–
UHD gamut in use
–
Saturation gain across every α for this image. The dashed line is the published method; the solid line uses your β.

The problem

A display can only show colours inside its gamut, the triangle formed by its red, green and blue primaries on the CIE 1931 chromaticity diagram. HD uses BT.709; UHD uses BT.2020, which covers about 1.9× the area. HD content on a UHD panel stays inside the small triangle, leaving almost half of the panel’s colour unused.

The published method

Azimi et al. (IEEE ICC 2017) move each pixel along the line from the white point through its own colour, so hue never changes. Colours inside an inner gamut (HD scaled by α) stay put to protect skin tones and neutrals; everything outside it is stretched to the UHD border:

x = xm + (D − d) · (xm − dα) / (d − dα),   with dα = α · d

Here xm is the pixel’s distance from the white point, and D and d are the distances to the UHD and HD borders along the same line.

What I changed

Real images sit in a small cloud near the white point, far inside the HD border. So almost no pixel lies outside the inner gamut until α drops below about 0.6, and the pixels that do move are measured against the full HD distance d, so they barely shift.

I added β, which shrinks the reference HD gamut towards the white point until it hugs the image’s own colours, and used this β·HD gamut in place of HD everywhere:

dβ = (1 − β) · d    dα = α · dβ    x = xm + (D − dβ) · (xm − dα) / (dβ − dα)

With β = 0 it is exactly the published method. With β > 0, every α does something, expansion is several times stronger, and colours spread more evenly towards the UHD border. I presented the results to the lab and Korean graduate students, and a paper was submitted to IET Electronics Letters (2018).

Details

  • Where: International research internship, Imaging Systems Lab, Changwon National University, South Korea, May–July 2018, funded by the Government of South Korea, under Prof. Oh-Seol Kwon.
  • Built with: MATLAB (original research code); Python/NumPy reference implementation; this page recreates the algorithm in JavaScript.
  • Code: github.com/cyberdrk/Color, with the MATLAB scripts, Python port, figures and full write-up.
  • Test images: Kodak Lossless True Color Image Suite and a standard ski-scene colour test image.