Skip to content

Commit

Permalink
Remove non-standard Jab translation of CAM16 JMh (#379)
Browse files Browse the repository at this point in the history
This is not an official representation of CAM16, and there is no
indication of what `a` and `b` are based off of: M, C, or s.

Currently, people should use CAM16 UCS, SCD, or LCD for a CAM16 Jab
space.
  • Loading branch information
facelessuser authored Nov 29, 2023
1 parent e403018 commit 56d7637
Show file tree
Hide file tree
Showing 15 changed files with 70 additions and 263 deletions.
8 changes: 0 additions & 8 deletions .coveragerc

This file was deleted.

2 changes: 0 additions & 2 deletions coloraide/everything.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
from .spaces.acescg import ACEScg
from .spaces.acescc import ACEScc
from .spaces.acescct import ACEScct
from .spaces.cam16 import CAM16
from .spaces.cam16_jmh import CAM16JMh
from .spaces.cam16_ucs import CAM16UCS, CAM16LCD, CAM16SCD
from .spaces.hct import HCT
Expand Down Expand Up @@ -89,7 +88,6 @@ class ColorAll(Base):
ACEScg(),
ACEScc(),
ACEScct(),
CAM16(),
CAM16JMh(),
CAM16UCS(),
CAM16SCD(),
Expand Down
76 changes: 44 additions & 32 deletions coloraide/spaces/cam16_ucs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
"""
from __future__ import annotations
import math
from . cam16 import CAM16
from ..types import Vector
from .cam16_jmh import CAM16JMh
from ..spaces import Space, Labish
from ..cat import WHITES
from .. import util
from ..channels import Channel, FLG_MIRROR_PERCENT
from .. import algebra as alg
from ..types import Vector

COEFFICENTS = {
'lcd': (0.77, 0.007, 0.0053),
Expand All @@ -19,25 +21,21 @@
}


def cam16_to_cam16_ucs(jab: Vector, model: str) -> Vector:
def cam16_jmh_to_cam16_ucs(jmh: Vector, model: str) -> Vector:
"""
CAM16 (Jab) to CAM16 UCS (Jab).
We can actually go between simply by removing the old colorfulness multiplier
and then adding the new adjusted multiplier. Then we can just adjust lightness.
"""

J, a, b = jab
M = math.sqrt(a ** 2 + b ** 2)
J, M, h = jmh

c1, c2 = COEFFICENTS[model][1:]

if M != 0:
a /= M
b /= M
M = math.log(1 + c2 * M) / c2
a *= M
b *= M
M = math.log(1 + c2 * M) / c2
a = M * math.cos(math.radians(h))
b = M * math.sin(math.radians(h))

return [
(1 + 100 * c1) * J / (1 + c1 * J),
Expand All @@ -46,7 +44,7 @@ def cam16_to_cam16_ucs(jab: Vector, model: str) -> Vector:
]


def cam16_ucs_to_cam16(ucs: Vector, model: str) -> Vector:
def cam16_ucs_to_cam16_jmh(ucs: Vector, model: str) -> Vector:
"""
CAM16 UCS (Jab) to CAM16 (Jab).
Expand All @@ -55,53 +53,67 @@ def cam16_ucs_to_cam16(ucs: Vector, model: str) -> Vector:
"""

J, a, b = ucs
M = math.sqrt(a ** 2 + b ** 2)

c1, c2 = COEFFICENTS[model][1:]

if M != 0:
a /= M
b /= M
M = (math.exp(M * c2) - 1) / c2
a *= M
b *= M
M = math.sqrt(a ** 2 + b ** 2)
M = (math.exp(M * c2) - 1) / c2
h = math.degrees(math.atan2(b, a))

return [
J / (1 - c1 * (J - 100)),
a,
b
M,
util.constrain_hue(h)
]


class CAM16UCS(CAM16):
class CAM16UCS(Labish, Space):
"""CAM16 UCS (Jab) class."""

BASE = "cam16"
BASE = "cam16-jmh"
NAME = "cam16-ucs"
SERIALIZE = ("--cam16-ucs",)
MODEL = 'ucs'
CHANNELS = (
Channel("j", 0.0, 100.0, limit=(0.0, None)),
Channel("j", 0.0, 100.0),
Channel("a", -50.0, 50.0, flags=FLG_MIRROR_PERCENT),
Channel("b", -50.0, 50.0, flags=FLG_MIRROR_PERCENT)
)
CHANNEL_ALIASES = {
"lightness": "j"
}
WHITE = WHITES['2deg']['D65']
# Use the same environment as CAM16JMh
ENV = CAM16JMh.ENV
ACHROMATIC = CAM16JMh.ACHROMATIC

def resolve_channel(self, index: int, coords: Vector) -> float:
"""Resolve channels."""

if index in (1, 2):
if not math.isnan(coords[index]):
return coords[index]

return self.ACHROMATIC.get_ideal_ab(coords[0])[index - 1]

value = coords[index]
return self.channels[index].nans if math.isnan(value) else value

def is_achromatic(self, coords: Vector) -> bool:
"""Check if color is achromatic."""

j, a, b = cam16_ucs_to_cam16(coords, self.MODEL)
m, h = alg.rect_to_polar(a, b)
return coords[0] == 0.0 or self.ACHROMATIC.test(j, m, h)
j, m, h = cam16_ucs_to_cam16_jmh(coords, self.MODEL)
return j <= 0.0 or self.ACHROMATIC.test(j, m, h)

def to_base(self, coords: Vector) -> Vector:
"""To XYZ from CAM16."""
"""To CAM16 JMh from CAM16."""

return cam16_ucs_to_cam16(coords, self.MODEL)
return cam16_ucs_to_cam16_jmh(coords, self.MODEL)

def from_base(self, coords: Vector) -> Vector:
"""From XYZ to CAM16."""
"""From CAM16 JMh to CAM16."""

return cam16_to_cam16_ucs(coords, self.MODEL)
return cam16_jmh_to_cam16_ucs(coords, self.MODEL)


class CAM16LCD(CAM16UCS):
Expand Down
3 changes: 3 additions & 0 deletions docs/src/markdown/about/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- **NEW**: Deprecate non-standard CAM16 (Jab) space. People should use the standard CAM16 JMh or the CAM16 UCS, SCD,
or LCD Jab spaces. The non-standard Jab is still available via `coloraide.spaces.cam16.CAM16`, but it is no longer
available in `coloraide.everything` and will be removed at a future time.
- **FIX**: Much more accurate ICtCp matrices.
- **FIX**: Fix typing of deeply nested arrays in `algebra`.
- **FIX**: Fix issue with HCT undefined channel resolver.
Expand Down
92 changes: 0 additions & 92 deletions docs/src/markdown/colors/cam16.md

This file was deleted.

4 changes: 2 additions & 2 deletions docs/src/markdown/colors/cam16_jmh.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ A color appearance model (CAM) is a mathematical model that seeks to describe th
vision, i.e. viewing conditions under which the appearance of a color does not tally with the corresponding physical
measurement of the stimulus source.

[CAM16](./cam16.md) is a successor of CIECAM02 with various fixes and improvements. The model actually defines numerous
different attributes:
CAM16 is a successor of CIECAM02 with various fixes and improvements. The model actually defines numerous different
attributes:

Name | Description
---- | -----------
Expand Down
10 changes: 4 additions & 6 deletions docs/src/markdown/colors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ flowchart TB
xyz-d65 --- lab-d65 --- lch-d65
xyz-d65 --- cam16-jmh --- cam16
cam16 --- cam16-ucs
cam16 --- cam16-scd
cam16 --- cam16-lcd
xyz-d65 --- cam16-jmh
cam16-jmh --- cam16-ucs
cam16-jmh --- cam16-scd
cam16-jmh --- cam16-lcd
xyz-d65 --- hct
Expand Down Expand Up @@ -157,7 +157,6 @@ flowchart TB
acescg(ACEScg)
acescc(ACEScc)
acescct(ACEScct)
cam16(CAM16)
cam16-jmh(CAM16 JMh)
cam16-ucs(CAM16 UCS)
cam16-scd(CAM16 SCD)
Expand Down Expand Up @@ -217,7 +216,6 @@ flowchart TB
click acescg "./acescg/" _self
click acescc "./acescc/" _self
click acescct "./acescct/" _self
click cam16 "./cam16/" _self
click cam16-jmh "./cam16_jmh/" _self
click cam16-ucs "./cam16_ucs/" _self
click cam16-scd "./cam16_scd/" _self
Expand Down
2 changes: 1 addition & 1 deletion docs/src/markdown/contrast.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Color("blue").contrast("red", method='wcag21')
///

Google's Material Design uses a new color space called [HCT](./colors/hct.md). It uses the hue and chroma from
[CAM16](./colors/cam16.md) and the tone/lightness from CIELab. For contrast, they determined using tones that are
[CAM16](./colors/cam16_jmh.md) and the tone/lightness from CIELab. For contrast, they determined using tones that are
"far enough apart" in the HCT color space was a good indication of sufficient contrast. Since HCT tone is exactly the
same as CIELab's lightness (also known as L\*), we've referred to this approach as Lstar.

Expand Down
4 changes: 2 additions & 2 deletions docs/src/markdown/gamut.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ Much like the other LCh chroma reduction algorithms, HCT Chroma performs gamut m
[LCh Chroma](#lch-chroma) with the exception that it uses the HCT color space as the working LCh color space.

Google's Material Design uses a new color space called [HCT](./colors/hct.md). It uses the hue and chroma from
[CAM16 (JMh)](./colors/cam16.md) space and the tone/lightness from the [CIELab](./colors/lab_d65.md) space. HCT takes
advantage of the good hue preservation of CAM16 and has the better lightness predictability of CIELab. Using these
[CAM16 (JMh)](./colors/cam16_jmh.md) space and the tone/lightness from the [CIELab](./colors/lab_d65.md) space. HCT
takes advantage of the good hue preservation of CAM16 and has the better lightness predictability of CIELab. Using these
characteristics, the color space is adept at generating tonal palettes with predictable lightness. This makes it easier
to construct UIs with decent contrast. But to do this well, you must work in HCT and gamut map in HCT. For this reason,
the HCT Chroma gamut mapping method was added.
Expand Down
4 changes: 2 additions & 2 deletions docs/src/markdown/manipulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,8 @@ always be true.
This behavior can be seen in non cylindrical spaces as well, like the Lab form of CAM16.

```py play
Color('gray').convert('cam16')
Color('cam16', [43.042, NaN, NaN]).normalize(nans=False)
Color('gray').convert('cam16-ucs')
Color('cam16-ucs', [56.23, NaN, NaN]).normalize(nans=False)
```

The selected values may not always perfectly precise, but they are much better than blindly assuming zero.
Expand Down
1 change: 0 additions & 1 deletion docs/src/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ nav:
- IPT: colors/ipt.md
- ICtCp: colors/ictcp.md
- IgPgTg: colors/igpgtg.md
- CAM16: colors/cam16.md
- CAM16 UCS: colors/cam16_ucs.md
- CAM16 SCD: colors/cam16_scd.md
- CAM16 LCD: colors/cam16_lcd.md
Expand Down
1 change: 0 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ nav:
- IPT: colors/ipt.md
- ICtCp: colors/ictcp.md
- IgPgTg: colors/igpgtg.md
- CAM16: colors/cam16.md
- CAM16 UCS: colors/cam16_ucs.md
- CAM16 SCD: colors/cam16_scd.md
- CAM16 LCD: colors/cam16_lcd.md
Expand Down
11 changes: 11 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ ignore = [
"RUF100"
]

[tool.coverage.report]
# Regexes for lines to exclude from consideration
omit = [
"coloraide/spaces/cam16.py"
]

exclude_lines = [
"pragma: no cover",
"@overload"
]

[tool.tox]
legacy_tox_ini = """
[tox]
Expand Down
Loading

0 comments on commit 56d7637

Please sign in to comment.