Skip to content

Commit 08dbd9e

Browse files
timholyjohnnychen94
andcommitted
Redesign color arithmetic
This notably defines 3 multiplication operators for RGB colors. It also un-defines `abs2`, because how that should work is a bit ambiguous. Finally, it defines a new `varmult` function, which allows one to compute variance using a specific multiplication operator. There are some compatibility definitions for current releases of ColorTypes. Co-authored-by: Johnny Chen <johnnychen94@hotmail.com>
1 parent 757d52e commit 08dbd9e

File tree

4 files changed

+419
-217
lines changed

4 files changed

+419
-217
lines changed

Project.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ColorVectorSpace"
22
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
3-
version = "0.8.7"
3+
version = "1.0.0"
44

55
[deps]
66
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
@@ -9,21 +9,20 @@ FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
99
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1010
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
1111
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
12-
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
12+
TensorCore = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
1313

1414
[compat]
1515
ColorTypes = "0.8, 0.9, 0.10"
1616
Colors = "0.9, 0.10, 0.11, 0.12"
1717
FixedPointNumbers = "0.6, 0.7, 0.8"
18-
SpecialFunctions = "0.7, 0.8, 0.9, 0.10, 1.0"
19-
StatsBase = "0.28, 0.29, 0.30, 0.31, 0.32, 0.33"
18+
SpecialFunctions = "0.7, 0.8, 0.9, 0.10"
19+
TensorCore = "0.1"
2020
julia = "1"
2121

2222
[extras]
2323
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
2424
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
25-
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
2625
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2726

2827
[targets]
29-
test = ["Statistics", "StatsBase", "LinearAlgebra", "Test"]
28+
test = ["Statistics", "LinearAlgebra", "Test"]

README.md

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,27 @@
66
This package is an add-on to [ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl), and provides fast
77
mathematical operations for objects with types such as `RGB` and
88
`Gray`.
9+
Specifically, with this package both grayscale and `RGB` colors are treated as if they are points
10+
in a normed vector space.
911

1012
## Introduction
1113

12-
Colorspaces such as RGB, unlike XYZ, are technically non-linear; the
13-
"colorimetrically correct" approach when averaging two RGBs is to
14+
Colorspaces such as RGB, unlike XYZ, are technically non-linear;
15+
perhaps the most "colorimetrically correct" approach when averaging two RGBs is to
1416
first convert each to XYZ, average them, and then convert back to RGB.
17+
Nor is there a clear definition of computing the sum of two colors.
18+
As a consequence, Julia's base color package,
19+
[ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl),
20+
does not support mathematical operations on colors.
1521

1622
However, particularly in image processing it is common to ignore this
1723
concern, and for the sake of performance treat an RGB as if it were a
18-
3-vector. This package provides such operations.
24+
3-vector. The role of this package is to extend ColorTypes to support such mathematical operations.
25+
Specifically, it defines `+` and multiplication by a scalar (and by extension, `-` and division by a scalar) for grayscale and `AbstractRGB` colors.
26+
These are the requirements of a [vector space](https://en.wikipedia.org/wiki/Vector_space).
1927

20-
If you're curious about how much difference it makes, the following
28+
If you're curious about how much the "colorimetrically correct" and
29+
"vector space" views differ, the following
2130
diagram might help. The first 10 `distinguishable_colors` were
2231
generated, and all pairs were averaged. Each box represents the
2332
average of the pair of diagonal elements intersected by tracing
@@ -27,16 +36,74 @@ represents the "RGB vector space" version.
2736

2837
![ColorVectorSpace](images/comparison.png "Comparison")
2938

39+
This package also defines `norm(c)` for RGB and grayscale colors.
40+
This makes these color spaces [normed vector spaces](https://en.wikipedia.org/wiki/Normed_vector_space).
41+
Note that `norm` has been designed to satisfy equivalence of grayscale and RGB representations: if
42+
`x` is a scalar, then `norm(x) == norm(Gray(x)) == norm(RGB(x, x, x))`.
43+
3044
## Usage
3145

3246
```julia
3347
using ColorTypes, ColorVectorSpace
3448
```
3549

36-
That's it. Just by loading `ColorVectorSpace`, most basic mathematical
50+
For the most part, that's it; just by loading `ColorVectorSpace`, most basic mathematical
3751
operations will "just work" on `AbstractRGB`, `AbstractGray`
3852
(`Color{T,1}`), `TransparentRGB`, and `TransparentGray` objects.
3953
(See definitions for the latter inside of `ColorTypes`).
4054

41-
If you discover missing operations, please open an issue, or better
42-
yet submit a pull request.
55+
However, there are some additional operations that you may need to distinguish carefully.
56+
57+
### Multiplication
58+
59+
Grayscale values are conceptually similar to scalars, and consequently it seems straightforward to define multiplication of two grayscale values.
60+
RGB values present more options.
61+
This package supports three different notions of multiplication: the inner product, the hadamard (elementwise) product, and the tensor product.
62+
63+
```julia
64+
julia> c1, c2 = RGB(0.2, 0.3, 0.4), RGB(0.5, 0.3, 0.2)
65+
(RGB{Float64}(0.2,0.3,0.4), RGB{Float64}(0.5,0.3,0.1))
66+
67+
julia> c1c2 # \cdot<TAB> # or dot(c1, c2)
68+
0.09000000000000001
69+
70+
# This is equivelant to `mapc(*, c1, c2)`
71+
julia> c1c2 # \odot<TAB> # or hadamard(c1, c2)
72+
RGB{Float64}(0.1,0.09,0.08000000000000002)
73+
74+
julia> c1c2 # \otimes<TAB> # or tensor(c1, c2)
75+
RGBRGB{Float64}(
76+
0.1 0.06 0.04000000000000001
77+
0.15 0.09 0.06
78+
0.2 0.12 0.08000000000000002)
79+
```
80+
81+
Note that `c1⋅c2 = (c1.r*c2.r + c1.g*c2.g + c1.b*c2.b)/3`, where the division by 3 ensures the equivalence `norm(x) == norm(Gray(x)) == norm(RGB(x, x, x))`.
82+
83+
It is designed to not support the ordinary multiplication operation `*` because it is not obvious which one of these should be the default option.
84+
85+
However, `*` is defined for grayscale since all these three multiplication operations (i.e., ``, `` and ``) are equivalent in the 1D vector space.
86+
87+
### Variance
88+
89+
The variance `v = E((c - μ)^2)` (or its bias-corrected version) involves a multiplication,
90+
and to be consistent with the above you must specify which sense of multiplication you wish to use:
91+
92+
```julia
93+
julia> cs = [c1, c2]
94+
2-element Array{RGB{Float64},1} with eltype RGB{Float64}:
95+
RGB{Float64}(0.2,0.3,0.4)
96+
RGB{Float64}(0.5,0.3,0.2)
97+
98+
julia> varmult(, cs)
99+
0.021666666666666667
100+
101+
julia> varmult(, cs)
102+
RGB{Float64}(0.045,0.0,0.020000000000000004)
103+
104+
julia> varmult(, cs)
105+
RGBRGB{Float64}(
106+
0.045 0.0 -0.03
107+
0.0 0.0 0.0
108+
-0.03 0.0 0.020000000000000004)
109+
```

0 commit comments

Comments
 (0)