@thi.ng/pixel-analysis 1.0.2 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/README.md +1 -1
- package/analyze-colors.d.ts +7 -4
- package/analyze-colors.js +31 -24
- package/analyze-features.d.ts +2 -7
- package/analyze-features.js +12 -5
- package/api.d.ts +56 -32
- package/hues.d.ts +30 -3
- package/hues.js +39 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-07-
|
|
3
|
+
- **Last updated**: 2025-07-27T16:56:27Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -11,6 +11,27 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
11
11
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
12
12
|
and/or version bumps of transitive dependencies.
|
|
13
13
|
|
|
14
|
+
### [1.1.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-analysis@1.1.1) (2025-07-27)
|
|
15
|
+
|
|
16
|
+
#### 🩹 Bug fixes
|
|
17
|
+
|
|
18
|
+
- update/fix weightedLuma result ([19d818c](https://github.com/thi-ng/umbrella/commit/19d818c))
|
|
19
|
+
|
|
20
|
+
## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-analysis@1.1.0) (2025-07-27)
|
|
21
|
+
|
|
22
|
+
#### 🚀 Features
|
|
23
|
+
|
|
24
|
+
- make temperature()/hueTemperature() parametric ([f61955e](https://github.com/thi-ng/umbrella/commit/f61955e))
|
|
25
|
+
- add coefficients as optional arg
|
|
26
|
+
- add `DEFAULT_TEMPERATURE_COEFFS`
|
|
27
|
+
- update analyzeColors() & ColorAnalysisResult ([595064d](https://github.com/thi-ng/umbrella/commit/595064d))
|
|
28
|
+
- update/rename `AnalysisOpts` => `ColorAnalysisOpts`
|
|
29
|
+
- add `ColorAnalysisOpts.tempCoeffs`
|
|
30
|
+
- rename `AnalyzedImage` => `ColorAnalysisResult`
|
|
31
|
+
- update `ColorAnalysisResult` internal structure
|
|
32
|
+
- add `computeHueRange()`
|
|
33
|
+
- update `analyzeFeatures()`, add `FeatureAnalysisResult` ([e276eb8](https://github.com/thi-ng/umbrella/commit/e276eb8))
|
|
34
|
+
|
|
14
35
|
### [1.0.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-analysis@1.0.1) (2025-07-24)
|
|
15
36
|
|
|
16
37
|
#### 🩹 Bug fixes
|
package/README.md
CHANGED
package/analyze-colors.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { FloatBuffer } from "@thi.ng/pixel/float";
|
|
2
2
|
import { IntBuffer } from "@thi.ng/pixel/int";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ColorAnalysisOpts, ColorAnalysisResult } from "./api.js";
|
|
4
|
+
import { type DEFAULT_TEMPERATURE_COEFFS } from "./hues.js";
|
|
4
5
|
/**
|
|
5
6
|
* Performs a set of image/color analyses on provided pixel buffer.
|
|
6
7
|
*
|
|
7
8
|
* @param img
|
|
8
9
|
* @param opts
|
|
9
10
|
*/
|
|
10
|
-
export declare const analyzeColors: (img: FloatBuffer | IntBuffer, opts?: Partial<
|
|
11
|
+
export declare const analyzeColors: (img: FloatBuffer | IntBuffer, opts?: Partial<ColorAnalysisOpts>) => ColorAnalysisResult;
|
|
11
12
|
/**
|
|
12
|
-
* Computes a number of metrics (partial {@link
|
|
13
|
+
* Computes a number of metrics (partial {@link ColorAnalysisResult}) derived from
|
|
13
14
|
* given raw SRGB colors. Helper function for {@link analyzeColors}.
|
|
14
15
|
*
|
|
15
16
|
* @param colors
|
|
17
|
+
* @param minSat
|
|
18
|
+
* @param tempCoeffs
|
|
16
19
|
*/
|
|
17
|
-
export declare const
|
|
20
|
+
export declare const derivedColorResults: (colors: number[][], minSat?: number, tempCoeffs?: typeof DEFAULT_TEMPERATURE_COEFFS) => Pick<ColorAnalysisResult, "css" | "srgb" | "hsv" | "oklch" | "hue" | "sat" | "luma" | "contrast" | "colorContrast" | "temperature">;
|
|
18
21
|
//# sourceMappingURL=analyze-colors.d.ts.map
|
package/analyze-colors.js
CHANGED
|
@@ -18,15 +18,18 @@ import { map } from "@thi.ng/transducers/map";
|
|
|
18
18
|
import { max } from "@thi.ng/transducers/max";
|
|
19
19
|
import { minMax } from "@thi.ng/transducers/min-max";
|
|
20
20
|
import { permutations } from "@thi.ng/transducers/permutations";
|
|
21
|
-
import { pluck } from "@thi.ng/transducers/pluck";
|
|
22
21
|
import { reduce } from "@thi.ng/transducers/reduce";
|
|
23
22
|
import { transduce } from "@thi.ng/transducers/transduce";
|
|
24
23
|
import { circularMean, circularSD } from "@thi.ng/vectors/circular";
|
|
25
24
|
import { dot } from "@thi.ng/vectors/dot";
|
|
26
25
|
import { vmean } from "@thi.ng/vectors/mean";
|
|
26
|
+
import { mulN } from "@thi.ng/vectors/muln";
|
|
27
27
|
import { roundN } from "@thi.ng/vectors/roundn";
|
|
28
28
|
import { sd } from "@thi.ng/vectors/variance";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
computeHueRange,
|
|
31
|
+
temperature
|
|
32
|
+
} from "./hues.js";
|
|
30
33
|
const analyzeColors = (img, opts) => {
|
|
31
34
|
let $img = img.format !== FLOAT_RGBA ? img.as(FLOAT_RGBA) : img;
|
|
32
35
|
if (opts?.size) $img = __resize($img, opts.size);
|
|
@@ -34,9 +37,13 @@ const analyzeColors = (img, opts) => {
|
|
|
34
37
|
const imgHsv = $img.as(FLOAT_HSVA);
|
|
35
38
|
const colors = __dominantColors($img, opts);
|
|
36
39
|
const colorAreas = colors.map((x) => x.area);
|
|
37
|
-
const derived =
|
|
40
|
+
const derived = derivedColorResults(
|
|
41
|
+
colors.map((x) => x.color),
|
|
42
|
+
opts?.minSat,
|
|
43
|
+
opts?.tempCoeffs
|
|
44
|
+
);
|
|
38
45
|
const lumaRangeImg = reduce(minMax(), imgGray.data);
|
|
39
|
-
const weightedLuma = dot(derived.
|
|
46
|
+
const weightedLuma = dot(derived.srgb.map(luminanceSrgb), colorAreas);
|
|
40
47
|
const weightedChroma = dot(
|
|
41
48
|
derived.oklch.map((x) => x[1]),
|
|
42
49
|
colorAreas
|
|
@@ -51,7 +58,7 @@ const analyzeColors = (img, opts) => {
|
|
|
51
58
|
imgHsv,
|
|
52
59
|
...derived,
|
|
53
60
|
area: colorAreas,
|
|
54
|
-
temperature: temperature(imgHsv, opts?.minSat),
|
|
61
|
+
temperature: temperature(imgHsv, opts?.minSat, opts?.tempCoeffs),
|
|
55
62
|
contrastImg: lumaRangeImg[1] - lumaRangeImg[0],
|
|
56
63
|
lumaRangeImg,
|
|
57
64
|
weightedSat,
|
|
@@ -59,37 +66,37 @@ const analyzeColors = (img, opts) => {
|
|
|
59
66
|
weightedChroma
|
|
60
67
|
};
|
|
61
68
|
};
|
|
62
|
-
const
|
|
69
|
+
const derivedColorResults = (colors, minSat, tempCoeffs) => {
|
|
63
70
|
const dominantLuma = colors.map((x) => luminanceSrgb(x));
|
|
64
71
|
const dominantSrgb = colors.map((x) => srgb(x));
|
|
65
72
|
const dominantHsv = dominantSrgb.map((x) => hsv(x));
|
|
66
73
|
const dominantOklch = dominantSrgb.map((x) => oklch(x));
|
|
67
74
|
const dominantCss = dominantSrgb.map((x) => css(x));
|
|
68
|
-
const
|
|
69
|
-
const
|
|
75
|
+
const hues = dominantHsv.map((x) => x[0]);
|
|
76
|
+
const huesRad = mulN([], hues, TAU);
|
|
70
77
|
const sats = dominantHsv.map((x) => x[1]);
|
|
71
|
-
const meanHue = circularMean(
|
|
72
|
-
const hueRange =
|
|
78
|
+
const meanHue = circularMean(huesRad) / TAU;
|
|
79
|
+
const hueRange = computeHueRange(hues, meanHue);
|
|
73
80
|
const lumaRange = reduce(minMax(), dominantLuma);
|
|
74
81
|
return {
|
|
75
82
|
css: dominantCss,
|
|
76
83
|
srgb: dominantSrgb,
|
|
77
84
|
hsv: dominantHsv,
|
|
78
85
|
oklch: dominantOklch,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
hue: {
|
|
87
|
+
mean: meanHue,
|
|
88
|
+
range: hueRange,
|
|
89
|
+
sd: circularSD(huesRad) / TAU
|
|
83
90
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
sat: {
|
|
92
|
+
mean: vmean(sats),
|
|
93
|
+
range: reduce(minMax(), sats),
|
|
94
|
+
sd: sd(sats)
|
|
88
95
|
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
luma: {
|
|
97
|
+
mean: vmean(dominantLuma),
|
|
98
|
+
range: lumaRange,
|
|
99
|
+
sd: sd(dominantLuma)
|
|
93
100
|
},
|
|
94
101
|
contrast: lumaRange[1] - lumaRange[0],
|
|
95
102
|
colorContrast: fit(
|
|
@@ -103,7 +110,7 @@ const derivedColorsResult = (colors, minSat) => {
|
|
|
103
110
|
0,
|
|
104
111
|
1
|
|
105
112
|
),
|
|
106
|
-
temperature: temperature(dominantHsv, minSat)
|
|
113
|
+
temperature: temperature(dominantHsv, minSat, tempCoeffs)
|
|
107
114
|
};
|
|
108
115
|
};
|
|
109
116
|
const __dominantColors = (img, {
|
|
@@ -120,5 +127,5 @@ const __resize = ($img, size) => {
|
|
|
120
127
|
};
|
|
121
128
|
export {
|
|
122
129
|
analyzeColors,
|
|
123
|
-
|
|
130
|
+
derivedColorResults
|
|
124
131
|
};
|
package/analyze-features.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { type FloatBuffer } from "@thi.ng/pixel/float";
|
|
2
2
|
import type { IntBuffer } from "@thi.ng/pixel/int";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
imgSobelX: FloatBuffer;
|
|
6
|
-
imgSobelY: FloatBuffer;
|
|
7
|
-
scoreEdge: number;
|
|
8
|
-
scoreSobel: number;
|
|
9
|
-
};
|
|
3
|
+
import type { FeatureAnalysisResult } from "./api.ts";
|
|
4
|
+
export declare const analyzeFeatures: (img: FloatBuffer | IntBuffer) => FeatureAnalysisResult;
|
|
10
5
|
//# sourceMappingURL=analyze-features.d.ts.map
|
package/analyze-features.js
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import {} from "@thi.ng/pixel/float";
|
|
8
8
|
import { FLOAT_GRAY } from "@thi.ng/pixel/format/float-gray";
|
|
9
9
|
import { FLOAT_GRAY_RANGE } from "@thi.ng/pixel/format/float-gray-range";
|
|
10
|
+
import { magSq } from "@thi.ng/vectors/magsq";
|
|
11
|
+
const { sqrt } = Math;
|
|
10
12
|
const FMT_EDGE = FLOAT_GRAY_RANGE(-24, 24);
|
|
11
13
|
const FMT_SOBEL = FLOAT_GRAY_RANGE(-4, 4);
|
|
12
14
|
const analyzeFeatures = (img) => {
|
|
@@ -19,15 +21,20 @@ const analyzeFeatures = (img) => {
|
|
|
19
21
|
imgSobelX.format = FMT_SOBEL;
|
|
20
22
|
const imgSobelY = convolveImage($img, { kernel: SOBEL_Y });
|
|
21
23
|
imgSobelY.format = FMT_SOBEL;
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
24
|
+
const edge = sqrt(magSq(imgEdge.data)) / numPixels;
|
|
25
|
+
const sobX = magSq(imgSobelX.data);
|
|
26
|
+
const sobY = magSq(imgSobelY.data);
|
|
27
|
+
const sobelX = sqrt(sobX) / numPixels;
|
|
28
|
+
const sobelY = sqrt(sobY) / numPixels;
|
|
29
|
+
const sobel = sqrt(sobX + sobY) / numPixels;
|
|
25
30
|
return {
|
|
26
31
|
imgEdge,
|
|
27
32
|
imgSobelX,
|
|
28
33
|
imgSobelY,
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
edge,
|
|
35
|
+
sobelX,
|
|
36
|
+
sobelY,
|
|
37
|
+
sobel
|
|
31
38
|
};
|
|
32
39
|
};
|
|
33
40
|
export {
|
package/api.d.ts
CHANGED
|
@@ -2,7 +2,12 @@ import type { Fn2 } from "@thi.ng/api";
|
|
|
2
2
|
import type { HSV, Oklch, SRGB } from "@thi.ng/color";
|
|
3
3
|
import type { FloatBuffer } from "@thi.ng/pixel";
|
|
4
4
|
import type { DominantColor } from "@thi.ng/pixel-dominant-colors";
|
|
5
|
-
|
|
5
|
+
import type { DEFAULT_TEMPERATURE_COEFFS } from "./hues.js";
|
|
6
|
+
/**
|
|
7
|
+
* 2-tuple representing a closed [min,max] value range/interval
|
|
8
|
+
*/
|
|
9
|
+
export type Range = [number, number];
|
|
10
|
+
export interface ColorAnalysisOpts {
|
|
6
11
|
/**
|
|
7
12
|
* Max. number of dominant colors.
|
|
8
13
|
*/
|
|
@@ -20,6 +25,11 @@ export interface AnalysisOpts {
|
|
|
20
25
|
* Min. saturation to consider for computing {@link temperature}.
|
|
21
26
|
*/
|
|
22
27
|
minSat: number;
|
|
28
|
+
/**
|
|
29
|
+
* Temperature curve hues/coefficients for computing {@link temparature}.
|
|
30
|
+
* See {@link DEFAULT_TEMPERATURE_COEFFS} for details.
|
|
31
|
+
*/
|
|
32
|
+
tempCoeffs: typeof DEFAULT_TEMPERATURE_COEFFS;
|
|
23
33
|
/**
|
|
24
34
|
* Channel precision for dominant colors.
|
|
25
35
|
*
|
|
@@ -27,17 +37,20 @@ export interface AnalysisOpts {
|
|
|
27
37
|
*/
|
|
28
38
|
prec: number;
|
|
29
39
|
}
|
|
30
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Result data structure returned by {@link analyzeColors}.
|
|
42
|
+
*/
|
|
43
|
+
export interface ColorAnalysisResult {
|
|
31
44
|
/**
|
|
32
45
|
* Input image, possibly converted to float RGBA & resized.
|
|
33
46
|
*/
|
|
34
47
|
img: FloatBuffer;
|
|
35
48
|
/**
|
|
36
|
-
* Float grayscale version of {@link
|
|
49
|
+
* Float grayscale version of {@link ColorAnalysisResult.img}.
|
|
37
50
|
*/
|
|
38
51
|
imgGray: FloatBuffer;
|
|
39
52
|
/**
|
|
40
|
-
* Float HSV version of {@link
|
|
53
|
+
* Float HSV version of {@link ColorAnalysisResult.img}.
|
|
41
54
|
*/
|
|
42
55
|
imgHsv: FloatBuffer;
|
|
43
56
|
/**
|
|
@@ -60,72 +73,74 @@ export interface AnalyzedImage {
|
|
|
60
73
|
* Normalized areas of dominant color clusters
|
|
61
74
|
*/
|
|
62
75
|
area: number[];
|
|
63
|
-
|
|
76
|
+
hue: {
|
|
64
77
|
/**
|
|
65
78
|
* Normalized mean hue (using circular mean).
|
|
66
79
|
*/
|
|
67
|
-
|
|
80
|
+
mean: number;
|
|
68
81
|
/**
|
|
69
|
-
*
|
|
82
|
+
* Min/max HSV hue range of dominant colors. IMPORTANT: In case of
|
|
83
|
+
* circular overflow (360 => 0 degrees), the min hue WILL be greater
|
|
84
|
+
* than the max hue (e.g. a hue range of `[0.8, 0.2]` indicates the hue
|
|
85
|
+
* range from magenta -> orange). Also see {@link ColorAnalysisResult.hue.mean}.
|
|
70
86
|
*/
|
|
71
|
-
|
|
87
|
+
range: Range;
|
|
72
88
|
/**
|
|
73
|
-
*
|
|
89
|
+
* Circular standard deviation of normalized hues.
|
|
74
90
|
*/
|
|
75
|
-
|
|
91
|
+
sd: number;
|
|
76
92
|
};
|
|
77
|
-
|
|
93
|
+
sat: {
|
|
78
94
|
/**
|
|
79
|
-
*
|
|
95
|
+
* Mean saturation
|
|
80
96
|
*/
|
|
81
|
-
|
|
97
|
+
mean: number;
|
|
82
98
|
/**
|
|
83
|
-
*
|
|
99
|
+
* Min/max HSV saturation range of dominant colors
|
|
84
100
|
*/
|
|
85
|
-
|
|
101
|
+
range: Range;
|
|
86
102
|
/**
|
|
87
|
-
* Standard deviation of normalized
|
|
103
|
+
* Standard deviation of normalized saturation.
|
|
88
104
|
*/
|
|
89
|
-
|
|
105
|
+
sd: number;
|
|
90
106
|
};
|
|
91
|
-
|
|
107
|
+
luma: {
|
|
92
108
|
/**
|
|
93
|
-
*
|
|
94
|
-
* circular overflow (360 => 0 degrees), the min hue WILL be greater
|
|
95
|
-
* than the max hue (e.g. a hue range of `[0.8, 0.2]` indicates the hue
|
|
96
|
-
* range from magenta -> orange). Also see {@link AnalyzedImage.mean.hue}.
|
|
109
|
+
* Mean luminance
|
|
97
110
|
*/
|
|
98
|
-
|
|
111
|
+
mean: number;
|
|
99
112
|
/**
|
|
100
|
-
* Min/max
|
|
113
|
+
* Min/max luminance range of dominant colors (obtained from SRGB)
|
|
101
114
|
*/
|
|
102
|
-
|
|
115
|
+
range: Range;
|
|
103
116
|
/**
|
|
104
|
-
*
|
|
117
|
+
* Standard deviation of normalized luminance.
|
|
105
118
|
*/
|
|
106
|
-
|
|
119
|
+
sd: number;
|
|
107
120
|
};
|
|
108
121
|
/**
|
|
109
122
|
* Min/max luminance range of entire grayscale image (obtained from SRGB)
|
|
110
123
|
*/
|
|
111
|
-
lumaRangeImg:
|
|
124
|
+
lumaRangeImg: Range;
|
|
112
125
|
/**
|
|
113
126
|
* Comprehensive {@link TemperatureResult} as produced by
|
|
114
|
-
* {@link temperature}. Also see {@link
|
|
127
|
+
* {@link temperature}. Also see {@link ColorAnalysisOpts.minSat} and
|
|
128
|
+
* {@link ColorAnalysisOpts.tempCoeffs}.
|
|
115
129
|
*/
|
|
116
130
|
temperature: TemperatureResult;
|
|
117
131
|
/**
|
|
118
132
|
* Luminance contrast of dominant colors (i.e. delta of
|
|
119
|
-
* {@link
|
|
133
|
+
* {@link ColorAnalysisResult.lumaRange}).
|
|
120
134
|
*/
|
|
121
135
|
contrast: number;
|
|
122
136
|
/**
|
|
123
137
|
* Luminance contrast of entire grayscale image (i.e. delta of
|
|
124
|
-
* {@link
|
|
138
|
+
* {@link ColorAnalysisResult.lumaRangeImg}).
|
|
125
139
|
*/
|
|
126
140
|
contrastImg: number;
|
|
127
141
|
/**
|
|
128
|
-
* Max. normalized WCAG color contrast of dominant colors.
|
|
142
|
+
* Max. normalized WCAG color contrast of dominant colors. The original WCAG
|
|
143
|
+
* result range [1,21] is rescaled to [0,1].
|
|
129
144
|
*/
|
|
130
145
|
colorContrast: number;
|
|
131
146
|
/**
|
|
@@ -171,4 +186,13 @@ export interface TemperatureResult {
|
|
|
171
186
|
*/
|
|
172
187
|
area: number;
|
|
173
188
|
}
|
|
189
|
+
export interface FeatureAnalysisResult {
|
|
190
|
+
imgEdge: FloatBuffer;
|
|
191
|
+
imgSobelX: FloatBuffer;
|
|
192
|
+
imgSobelY: FloatBuffer;
|
|
193
|
+
edge: number;
|
|
194
|
+
sobel: number;
|
|
195
|
+
sobelX: number;
|
|
196
|
+
sobelY: number;
|
|
197
|
+
}
|
|
174
198
|
//# sourceMappingURL=api.d.ts.map
|
package/hues.d.ts
CHANGED
|
@@ -86,10 +86,20 @@ export declare const hueRangeAreaIntensity: (colors: Iterable<ReadonlyVec>, hueR
|
|
|
86
86
|
* @param colors
|
|
87
87
|
*/
|
|
88
88
|
export declare const meanIntensity: (colors: Iterable<ReadonlyVec>) => number;
|
|
89
|
+
/**
|
|
90
|
+
* Constructs the min/max range of given normalized `hues` around given mean hue
|
|
91
|
+
* (also normalized), considering circular wrap-around. If `min > max` in the
|
|
92
|
+
* result `[min,max]`, then the hue range is crossing 0 degrees.
|
|
93
|
+
*
|
|
94
|
+
* @param hues
|
|
95
|
+
* @param range
|
|
96
|
+
* @param mean
|
|
97
|
+
*/
|
|
98
|
+
export declare const computeHueRange: (hues: number[], mean: number) => [number, number];
|
|
89
99
|
/**
|
|
90
100
|
* Computes an abstract measure of a normalized "color temperature" of the given
|
|
91
101
|
* HSV `colors` (also normalized in [0,1] range). Results closer to 1.0 indicate
|
|
92
|
-
* a prevalence of warmer colors, results closer to -1.0 mean more
|
|
102
|
+
* a prevalence of warmer (red/orange) colors, results closer to -1.0 mean more
|
|
93
103
|
* colder/blue-ish colors present.
|
|
94
104
|
*
|
|
95
105
|
* @remarks
|
|
@@ -109,8 +119,24 @@ export declare const meanIntensity: (colors: Iterable<ReadonlyVec>) => number;
|
|
|
109
119
|
*
|
|
110
120
|
* @param colors
|
|
111
121
|
* @param minSat
|
|
122
|
+
* @param coeffs
|
|
123
|
+
*/
|
|
124
|
+
export declare const temperature: (colors: Iterable<ReadonlyVec>, minSat?: number, coeffs?: typeof DEFAULT_TEMPERATURE_COEFFS) => TemperatureResult;
|
|
125
|
+
/**
|
|
126
|
+
* Default temperature curve hues/coefficients for {@link hueTemperature}.
|
|
127
|
+
*
|
|
128
|
+
* @remarks
|
|
129
|
+
* The four values (all in [0,1] range) are to be giving in this order:
|
|
130
|
+
*
|
|
131
|
+
* - A: start hue of warm falloff
|
|
132
|
+
* - B: end hue of warm falloff
|
|
133
|
+
* - C: start hue of warm rise
|
|
134
|
+
* - D: end hue of warm rise
|
|
135
|
+
*
|
|
136
|
+
* Note: The hot point is fixed at 0 (aka red) and the cold point is fixed at
|
|
137
|
+
* 2/3 (= 0.666... aka blue), meaning C & D MUST be greater.
|
|
112
138
|
*/
|
|
113
|
-
export declare const
|
|
139
|
+
export declare const DEFAULT_TEMPERATURE_COEFFS: [number, number, number, number];
|
|
114
140
|
/**
|
|
115
141
|
* Computes an abstract measure of a normalized "color temperature" ([-1,1]
|
|
116
142
|
* range) for the given `hue` (in [0,1] range). Red/orange/yellow hues produce
|
|
@@ -122,6 +148,7 @@ export declare const temperature: (colors: Iterable<ReadonlyVec>, minSat?: numbe
|
|
|
122
148
|
* input hue.
|
|
123
149
|
*
|
|
124
150
|
* @param hue
|
|
151
|
+
* @param coeffs
|
|
125
152
|
*/
|
|
126
|
-
export declare const hueTemperature: (hue: number) => number;
|
|
153
|
+
export declare const hueTemperature: (hue: number, [a, b, c, d]?: [number, number, number, number]) => number;
|
|
127
154
|
//# sourceMappingURL=hues.d.ts.map
|
package/hues.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { ensureArray } from "@thi.ng/arrays/ensure-array";
|
|
2
2
|
import { compareByKey } from "@thi.ng/compare";
|
|
3
|
-
import {
|
|
3
|
+
import { TAU } from "@thi.ng/math/api";
|
|
4
4
|
import { normFrequenciesAuto, repeat, transduce } from "@thi.ng/transducers";
|
|
5
5
|
import { map } from "@thi.ng/transducers/map";
|
|
6
6
|
import { mapcat } from "@thi.ng/transducers/mapcat";
|
|
7
7
|
import { mean } from "@thi.ng/transducers/mean";
|
|
8
|
+
import { minMax } from "@thi.ng/transducers/min-max";
|
|
9
|
+
import { reduce } from "@thi.ng/transducers/reduce";
|
|
8
10
|
import { circularMean } from "@thi.ng/vectors/circular";
|
|
11
|
+
import { fract, roundTo } from "@thi.ng/math/prec";
|
|
12
|
+
import { smoothStep } from "@thi.ng/math/step";
|
|
9
13
|
function* selectHueRange(colors, minHue, maxHue, minSat) {
|
|
10
14
|
const pred = __hueSelector(minHue, maxHue);
|
|
11
15
|
for (let col of colors) {
|
|
@@ -60,7 +64,30 @@ const meanIntensity = (colors) => transduce(
|
|
|
60
64
|
mean(),
|
|
61
65
|
colors
|
|
62
66
|
);
|
|
63
|
-
const
|
|
67
|
+
const computeHueRange = (hues, mean2) => {
|
|
68
|
+
const range = reduce(minMax(), hues);
|
|
69
|
+
const [min, max] = range;
|
|
70
|
+
if (mean2 < min || mean2 > max) {
|
|
71
|
+
return [
|
|
72
|
+
hues.reduce(
|
|
73
|
+
(acc, x) => {
|
|
74
|
+
const d = fract(mean2 - x);
|
|
75
|
+
return d < 0.5 && d > acc[1] ? [x, d] : acc;
|
|
76
|
+
},
|
|
77
|
+
[max, fract(mean2 - max)]
|
|
78
|
+
)[0],
|
|
79
|
+
hues.reduce(
|
|
80
|
+
(acc, x) => {
|
|
81
|
+
const d = fract(x - mean2);
|
|
82
|
+
return d < 0.5 && d > acc[1] ? [x, d] : acc;
|
|
83
|
+
},
|
|
84
|
+
[min, fract(min - mean2)]
|
|
85
|
+
)[0]
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
return range;
|
|
89
|
+
};
|
|
90
|
+
const temperature = (colors, minSat = 0.2, coeffs) => {
|
|
64
91
|
const $colors = ensureArray(colors);
|
|
65
92
|
const filtered = $colors.filter((x) => x[1] >= minSat);
|
|
66
93
|
const area = filtered.length / $colors.length;
|
|
@@ -81,12 +108,20 @@ const temperature = (colors, minSat = 0.2) => {
|
|
|
81
108
|
return { hues, meanHue: 0, temp: 0, areaTemp: 0, area: 0 };
|
|
82
109
|
}
|
|
83
110
|
const meanHue = circularMean(angles) / TAU;
|
|
84
|
-
const temp = hueTemperature(meanHue);
|
|
111
|
+
const temp = hueTemperature(meanHue, coeffs);
|
|
85
112
|
const areaTemp = temp * area;
|
|
86
113
|
return { hues, meanHue, temp, areaTemp, area };
|
|
87
114
|
};
|
|
88
|
-
const
|
|
115
|
+
const DEFAULT_TEMPERATURE_COEFFS = [
|
|
116
|
+
0.1,
|
|
117
|
+
0.6,
|
|
118
|
+
0.72,
|
|
119
|
+
0.92
|
|
120
|
+
];
|
|
121
|
+
const hueTemperature = (hue, [a, b, c, d] = DEFAULT_TEMPERATURE_COEFFS) => 2 * (hue < 2 / 3 ? smoothStep(b, a, hue) : smoothStep(c, d, hue)) - 1;
|
|
89
122
|
export {
|
|
123
|
+
DEFAULT_TEMPERATURE_COEFFS,
|
|
124
|
+
computeHueRange,
|
|
90
125
|
countHueRange,
|
|
91
126
|
hueRangeArea,
|
|
92
127
|
hueRangeAreaIntensity,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/pixel-analysis",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Image color & feature analysis utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"status": "beta",
|
|
113
113
|
"year": 2024
|
|
114
114
|
},
|
|
115
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "77f9f42528ac3c6209429fb798e3be69f52fcff2\n"
|
|
116
116
|
}
|