@thi.ng/pixel-analysis 1.1.2 → 2.0.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 +14 -1
- package/README.md +3 -2
- package/analyze-colors.d.ts +7 -4
- package/analyze-colors.js +57 -52
- package/analyze-features.js +2 -2
- package/api.d.ts +68 -81
- package/hues.d.ts +3 -4
- package/hues.js +5 -28
- package/package.json +13 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-07-
|
|
3
|
+
- **Last updated**: 2025-07-30T22:32:35Z
|
|
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,19 @@ 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
|
+
# [2.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-analysis@2.0.0) (2025-07-30)
|
|
15
|
+
|
|
16
|
+
#### 🛑 Breaking changes
|
|
17
|
+
|
|
18
|
+
- update color analysis result types, add aggregation ([8c70504](https://github.com/thi-ng/umbrella/commit/8c70504))
|
|
19
|
+
- BREAKING CHANGE: update color analysis result types, add aggregation
|
|
20
|
+
- add `BaseColorAnalysisResult`, `AggregatedColorAnalysisResult`
|
|
21
|
+
- update `ColorAnalysisResult`, use `Metrics`
|
|
22
|
+
- add `aggregateColorResults()`
|
|
23
|
+
- update `analyzeColors()`, `deriveColorResults()`
|
|
24
|
+
- rename `computeHueRange()` => `hueRange()`
|
|
25
|
+
- update deps
|
|
26
|
+
|
|
14
27
|
### [1.1.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-analysis@1.1.1) (2025-07-27)
|
|
15
28
|
|
|
16
29
|
#### 🩹 Bug fixes
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://mastodon.thi.ng/@toxi)
|
|
8
8
|
|
|
9
9
|
> [!NOTE]
|
|
10
|
-
> This is one of
|
|
10
|
+
> This is one of 210 standalone projects, maintained as part
|
|
11
11
|
> of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
|
|
12
12
|
> and anti-framework.
|
|
13
13
|
>
|
|
@@ -81,7 +81,7 @@ For Node.js REPL:
|
|
|
81
81
|
const pa = await import("@thi.ng/pixel-analysis");
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 1.
|
|
84
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.87 KB
|
|
85
85
|
|
|
86
86
|
## Dependencies
|
|
87
87
|
|
|
@@ -90,6 +90,7 @@ Package sizes (brotli'd, pre-treeshake): ESM: 1.90 KB
|
|
|
90
90
|
- [@thi.ng/color](https://github.com/thi-ng/umbrella/tree/develop/packages/color)
|
|
91
91
|
- [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/develop/packages/compare)
|
|
92
92
|
- [@thi.ng/math](https://github.com/thi-ng/umbrella/tree/develop/packages/math)
|
|
93
|
+
- [@thi.ng/metrics](https://github.com/thi-ng/umbrella/tree/develop/packages/metrics)
|
|
93
94
|
- [@thi.ng/pixel](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel)
|
|
94
95
|
- [@thi.ng/pixel-convolve](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel-convolve)
|
|
95
96
|
- [@thi.ng/pixel-dominant-colors](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel-dominant-colors)
|
package/analyze-colors.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FloatBuffer } from "@thi.ng/pixel/float";
|
|
2
2
|
import { IntBuffer } from "@thi.ng/pixel/int";
|
|
3
|
-
import type { ColorAnalysisOpts, ColorAnalysisResult } from "./api.js";
|
|
3
|
+
import type { AggregatedColorAnalysisResult, BaseColorAnalysisResult, ColorAnalysisOpts, ColorAnalysisResult } from "./api.js";
|
|
4
4
|
import { type DEFAULT_TEMPERATURE_COEFFS } from "./hues.js";
|
|
5
5
|
/**
|
|
6
6
|
* Performs a set of image/color analyses on provided pixel buffer.
|
|
@@ -10,12 +10,15 @@ import { type DEFAULT_TEMPERATURE_COEFFS } from "./hues.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export declare const analyzeColors: (img: FloatBuffer | IntBuffer, opts?: Partial<ColorAnalysisOpts>) => ColorAnalysisResult;
|
|
12
12
|
/**
|
|
13
|
-
* Computes a number of metrics (partial {@link ColorAnalysisResult}) derived
|
|
14
|
-
* given raw SRGB colors. Helper function for
|
|
13
|
+
* Computes a number of metrics (partial {@link ColorAnalysisResult}) derived
|
|
14
|
+
* from given raw SRGB colors and their (normalized) areas. Helper function for
|
|
15
|
+
* {@link analyzeColors}.
|
|
15
16
|
*
|
|
16
17
|
* @param colors
|
|
18
|
+
* @param areas
|
|
17
19
|
* @param minSat
|
|
18
20
|
* @param tempCoeffs
|
|
19
21
|
*/
|
|
20
|
-
export declare const
|
|
22
|
+
export declare const deriveColorResults: (colors: number[][], areas?: number[], minSat?: number, tempCoeffs?: typeof DEFAULT_TEMPERATURE_COEFFS) => BaseColorAnalysisResult;
|
|
23
|
+
export declare const aggregateColorResults: (results: Omit<ColorAnalysisResult, "img" | "imgGray" | "imgHsv">[], numColors?: number) => AggregatedColorAnalysisResult;
|
|
21
24
|
//# sourceMappingURL=analyze-colors.d.ts.map
|
package/analyze-colors.js
CHANGED
|
@@ -6,8 +6,16 @@ import { oklch } from "@thi.ng/color/oklch/oklch";
|
|
|
6
6
|
import { srgb } from "@thi.ng/color/srgb/srgb";
|
|
7
7
|
import { compareByKey } from "@thi.ng/compare/keys";
|
|
8
8
|
import { compareNumDesc } from "@thi.ng/compare/numeric";
|
|
9
|
-
import { TAU } from "@thi.ng/math/api";
|
|
10
9
|
import { fit } from "@thi.ng/math/fit";
|
|
10
|
+
import {
|
|
11
|
+
aggregateCircularMetrics,
|
|
12
|
+
aggregateMetrics,
|
|
13
|
+
aggregateWeightedMetrics,
|
|
14
|
+
defCircularMetric,
|
|
15
|
+
defMetric,
|
|
16
|
+
defWeightedMetric
|
|
17
|
+
} from "@thi.ng/metrics/metrics";
|
|
18
|
+
import { dominantColorsMeanCut } from "@thi.ng/pixel-dominant-colors";
|
|
11
19
|
import { dominantColorsKmeans } from "@thi.ng/pixel-dominant-colors/kmeans";
|
|
12
20
|
import { FloatBuffer } from "@thi.ng/pixel/float";
|
|
13
21
|
import { FLOAT_GRAY } from "@thi.ng/pixel/format/float-gray";
|
|
@@ -16,20 +24,11 @@ import { FLOAT_RGBA } from "@thi.ng/pixel/format/float-rgba";
|
|
|
16
24
|
import { IntBuffer } from "@thi.ng/pixel/int";
|
|
17
25
|
import { map } from "@thi.ng/transducers/map";
|
|
18
26
|
import { max } from "@thi.ng/transducers/max";
|
|
19
|
-
import { minMax } from "@thi.ng/transducers/min-max";
|
|
20
27
|
import { permutations } from "@thi.ng/transducers/permutations";
|
|
21
|
-
import { reduce } from "@thi.ng/transducers/reduce";
|
|
22
28
|
import { transduce } from "@thi.ng/transducers/transduce";
|
|
23
|
-
import { circularMean, circularSD } from "@thi.ng/vectors/circular";
|
|
24
|
-
import { dot } from "@thi.ng/vectors/dot";
|
|
25
|
-
import { vmean } from "@thi.ng/vectors/mean";
|
|
26
|
-
import { mulN } from "@thi.ng/vectors/muln";
|
|
27
29
|
import { roundN } from "@thi.ng/vectors/roundn";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
computeHueRange,
|
|
31
|
-
temperature
|
|
32
|
-
} from "./hues.js";
|
|
30
|
+
import { ones } from "@thi.ng/vectors/setn";
|
|
31
|
+
import { temperature } from "./hues.js";
|
|
33
32
|
const analyzeColors = (img, opts) => {
|
|
34
33
|
let $img = img.format !== FLOAT_RGBA ? img.as(FLOAT_RGBA) : img;
|
|
35
34
|
if (opts?.size) $img = __resize($img, opts.size);
|
|
@@ -37,68 +36,46 @@ const analyzeColors = (img, opts) => {
|
|
|
37
36
|
const imgHsv = $img.as(FLOAT_HSVA);
|
|
38
37
|
const colors = __dominantColors($img, opts);
|
|
39
38
|
const colorAreas = colors.map((x) => x.area);
|
|
40
|
-
const derived =
|
|
39
|
+
const derived = deriveColorResults(
|
|
41
40
|
colors.map((x) => x.color),
|
|
41
|
+
colorAreas,
|
|
42
42
|
opts?.minSat,
|
|
43
43
|
opts?.tempCoeffs
|
|
44
44
|
);
|
|
45
|
-
const
|
|
46
|
-
const weightedLuma = dot(derived.srgb.map(luminanceSrgb), colorAreas);
|
|
47
|
-
const weightedChroma = dot(
|
|
48
|
-
derived.oklch.map((x) => x[1]),
|
|
49
|
-
colorAreas
|
|
50
|
-
);
|
|
51
|
-
const weightedSat = dot(
|
|
52
|
-
derived.hsv.map((x) => x[1]),
|
|
53
|
-
colorAreas
|
|
54
|
-
);
|
|
45
|
+
const lumImg = defMetric(imgGray.data);
|
|
55
46
|
return {
|
|
47
|
+
...derived,
|
|
56
48
|
img: $img,
|
|
57
49
|
imgGray,
|
|
58
50
|
imgHsv,
|
|
59
|
-
|
|
60
|
-
area: colorAreas,
|
|
51
|
+
lumImg,
|
|
61
52
|
temperature: temperature(imgHsv, opts?.minSat, opts?.tempCoeffs),
|
|
62
|
-
contrastImg:
|
|
63
|
-
lumaRangeImg,
|
|
64
|
-
weightedSat,
|
|
65
|
-
weightedLuma,
|
|
66
|
-
weightedChroma
|
|
53
|
+
contrastImg: lumImg.max - lumImg.min
|
|
67
54
|
};
|
|
68
55
|
};
|
|
69
|
-
const
|
|
56
|
+
const deriveColorResults = (colors, areas = ones(colors.length), minSat, tempCoeffs) => {
|
|
70
57
|
const dominantLuma = colors.map((x) => luminanceSrgb(x));
|
|
71
58
|
const dominantSrgb = colors.map((x) => srgb(x));
|
|
72
59
|
const dominantHsv = dominantSrgb.map((x) => hsv(x));
|
|
73
60
|
const dominantOklch = dominantSrgb.map((x) => oklch(x));
|
|
74
61
|
const dominantCss = dominantSrgb.map((x) => css(x));
|
|
75
62
|
const hues = dominantHsv.map((x) => x[0]);
|
|
76
|
-
const huesRad = mulN([], hues, TAU);
|
|
77
63
|
const sats = dominantHsv.map((x) => x[1]);
|
|
78
|
-
const
|
|
79
|
-
const hueRange = computeHueRange(hues, meanHue);
|
|
80
|
-
const lumaRange = reduce(minMax(), dominantLuma);
|
|
64
|
+
const lum = defWeightedMetric(dominantLuma, areas);
|
|
81
65
|
return {
|
|
82
66
|
css: dominantCss,
|
|
83
67
|
srgb: dominantSrgb,
|
|
84
68
|
hsv: dominantHsv,
|
|
85
69
|
oklch: dominantOklch,
|
|
86
|
-
hue:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
},
|
|
96
|
-
luma: {
|
|
97
|
-
mean: vmean(dominantLuma),
|
|
98
|
-
range: lumaRange,
|
|
99
|
-
sd: sd(dominantLuma)
|
|
100
|
-
},
|
|
101
|
-
contrast: lumaRange[1] - lumaRange[0],
|
|
70
|
+
hue: defCircularMetric(hues),
|
|
71
|
+
sat: defWeightedMetric(sats, areas),
|
|
72
|
+
chroma: defWeightedMetric(
|
|
73
|
+
dominantOklch.map((x) => x[1]),
|
|
74
|
+
areas
|
|
75
|
+
),
|
|
76
|
+
lum,
|
|
77
|
+
areas,
|
|
78
|
+
contrast: lum.max - lum.min,
|
|
102
79
|
colorContrast: fit(
|
|
103
80
|
transduce(
|
|
104
81
|
map((pair) => contrastWCAG(...pair)),
|
|
@@ -113,6 +90,33 @@ const derivedColorResults = (colors, minSat, tempCoeffs) => {
|
|
|
113
90
|
temperature: temperature(dominantHsv, minSat, tempCoeffs)
|
|
114
91
|
};
|
|
115
92
|
};
|
|
93
|
+
const aggregateColorResults = (results, numColors = 4) => {
|
|
94
|
+
const dominant = dominantColorsMeanCut(
|
|
95
|
+
results.flatMap((res) => res.srgb),
|
|
96
|
+
numColors
|
|
97
|
+
).map((x) => srgb(x.color));
|
|
98
|
+
return {
|
|
99
|
+
srgb: dominant,
|
|
100
|
+
css: dominant.map((x) => css(x)),
|
|
101
|
+
hsv: dominant.map((x) => hsv(x)),
|
|
102
|
+
oklch: dominant.map((x) => oklch(x)),
|
|
103
|
+
hue: aggregateCircularMetrics(results.map((x) => x.hue)),
|
|
104
|
+
sat: aggregateWeightedMetrics(results.map((x) => x.sat)),
|
|
105
|
+
chroma: aggregateWeightedMetrics(results.map((x) => x.chroma)),
|
|
106
|
+
lum: aggregateWeightedMetrics(results.map((x) => x.lum)),
|
|
107
|
+
lumImg: aggregateMetrics(results.map((x) => x.lumImg)),
|
|
108
|
+
contrast: defMetric(results.map((x) => x.contrast)),
|
|
109
|
+
contrastImg: defMetric(results.map((x) => x.contrastImg)),
|
|
110
|
+
colorContrast: defMetric(results.map((x) => x.colorContrast)),
|
|
111
|
+
temperature: {
|
|
112
|
+
meanHue: defCircularMetric(
|
|
113
|
+
results.map((x) => x.temperature.meanHue)
|
|
114
|
+
),
|
|
115
|
+
temp: defMetric(results.map((x) => x.temperature.temp)),
|
|
116
|
+
areaTemp: defMetric(results.map((x) => x.temperature.areaTemp))
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
};
|
|
116
120
|
const __dominantColors = (img, {
|
|
117
121
|
dominantFn = dominantColorsKmeans,
|
|
118
122
|
numColors = 4,
|
|
@@ -126,6 +130,7 @@ const __resize = ($img, size) => {
|
|
|
126
130
|
return $img.resize(w, h);
|
|
127
131
|
};
|
|
128
132
|
export {
|
|
133
|
+
aggregateColorResults,
|
|
129
134
|
analyzeColors,
|
|
130
|
-
|
|
135
|
+
deriveColorResults
|
|
131
136
|
};
|
package/analyze-features.js
CHANGED
|
@@ -12,9 +12,9 @@ const { sqrt } = Math;
|
|
|
12
12
|
const FMT_EDGE = FLOAT_GRAY_RANGE(-24, 24);
|
|
13
13
|
const FMT_SOBEL = FLOAT_GRAY_RANGE(-4, 4);
|
|
14
14
|
const analyzeFeatures = (img) => {
|
|
15
|
-
const
|
|
16
|
-
const { width, height } = $img;
|
|
15
|
+
const { width, height } = img;
|
|
17
16
|
const numPixels = width * height;
|
|
17
|
+
const $img = img.format !== FLOAT_GRAY ? img.as(FLOAT_GRAY) : img;
|
|
18
18
|
const imgEdge = convolveImage($img, { kernel: EDGE5 });
|
|
19
19
|
imgEdge.format = FMT_EDGE;
|
|
20
20
|
const imgSobelX = convolveImage($img, { kernel: SOBEL_X });
|
package/api.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { Fn2 } from "@thi.ng/api";
|
|
2
2
|
import type { HSV, Oklch, SRGB } from "@thi.ng/color";
|
|
3
|
+
import type { Metric, WeightedMetric } from "@thi.ng/metrics";
|
|
3
4
|
import type { FloatBuffer } from "@thi.ng/pixel";
|
|
4
5
|
import type { DominantColor } from "@thi.ng/pixel-dominant-colors";
|
|
5
6
|
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
7
|
export interface ColorAnalysisOpts {
|
|
11
8
|
/**
|
|
12
9
|
* Max. number of dominant colors.
|
|
@@ -37,22 +34,7 @@ export interface ColorAnalysisOpts {
|
|
|
37
34
|
*/
|
|
38
35
|
prec: number;
|
|
39
36
|
}
|
|
40
|
-
|
|
41
|
-
* Result data structure returned by {@link analyzeColors}.
|
|
42
|
-
*/
|
|
43
|
-
export interface ColorAnalysisResult {
|
|
44
|
-
/**
|
|
45
|
-
* Input image, possibly converted to float RGBA & resized.
|
|
46
|
-
*/
|
|
47
|
-
img: FloatBuffer;
|
|
48
|
-
/**
|
|
49
|
-
* Float grayscale version of {@link ColorAnalysisResult.img}.
|
|
50
|
-
*/
|
|
51
|
-
imgGray: FloatBuffer;
|
|
52
|
-
/**
|
|
53
|
-
* Float HSV version of {@link ColorAnalysisResult.img}.
|
|
54
|
-
*/
|
|
55
|
-
imgHsv: FloatBuffer;
|
|
37
|
+
export interface BaseColorAnalysisResult {
|
|
56
38
|
/**
|
|
57
39
|
* Dominant colors as CSS (hex) strings
|
|
58
40
|
*/
|
|
@@ -70,58 +52,31 @@ export interface ColorAnalysisResult {
|
|
|
70
52
|
*/
|
|
71
53
|
oklch: Oklch[];
|
|
72
54
|
/**
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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}.
|
|
86
|
-
*/
|
|
87
|
-
range: Range;
|
|
88
|
-
/**
|
|
89
|
-
* Circular standard deviation of normalized hues.
|
|
90
|
-
*/
|
|
91
|
-
sd: number;
|
|
92
|
-
};
|
|
93
|
-
sat: {
|
|
94
|
-
/**
|
|
95
|
-
* Mean saturation
|
|
96
|
-
*/
|
|
97
|
-
mean: number;
|
|
98
|
-
/**
|
|
99
|
-
* Min/max HSV saturation range of dominant colors
|
|
100
|
-
*/
|
|
101
|
-
range: Range;
|
|
102
|
-
/**
|
|
103
|
-
* Standard deviation of normalized saturation.
|
|
104
|
-
*/
|
|
105
|
-
sd: number;
|
|
106
|
-
};
|
|
107
|
-
luma: {
|
|
108
|
-
/**
|
|
109
|
-
* Mean luminance
|
|
110
|
-
*/
|
|
111
|
-
mean: number;
|
|
112
|
-
/**
|
|
113
|
-
* Min/max luminance range of dominant colors (obtained from SRGB)
|
|
114
|
-
*/
|
|
115
|
-
range: Range;
|
|
116
|
-
/**
|
|
117
|
-
* Standard deviation of normalized luminance.
|
|
118
|
-
*/
|
|
119
|
-
sd: number;
|
|
120
|
-
};
|
|
55
|
+
* HSV hue metrics of dominant colors.
|
|
56
|
+
*
|
|
57
|
+
* @remarks
|
|
58
|
+
* The mean and standard deviation are computed using circular versions. In
|
|
59
|
+
* case of circular overflow (1.0 => 0, aka 360 => 0 degrees), the min hue
|
|
60
|
+
* WILL be greater than the max hue (e.g. a hue range of `[0.8, 0.2]`
|
|
61
|
+
* indicates the hue range from magenta -> orange).
|
|
62
|
+
*/
|
|
63
|
+
hue: Metric;
|
|
121
64
|
/**
|
|
122
|
-
*
|
|
65
|
+
* HSV saturation metrics of dominant colors.
|
|
123
66
|
*/
|
|
124
|
-
|
|
67
|
+
sat: WeightedMetric;
|
|
68
|
+
/**
|
|
69
|
+
* Oklch chroma metrics of dominant colors.
|
|
70
|
+
*/
|
|
71
|
+
chroma: WeightedMetric;
|
|
72
|
+
/**
|
|
73
|
+
* sRGB-based luminance metrics of dominant colors.
|
|
74
|
+
*/
|
|
75
|
+
lum: WeightedMetric;
|
|
76
|
+
/**
|
|
77
|
+
* Normalized areas of dominant colors
|
|
78
|
+
*/
|
|
79
|
+
areas: number[];
|
|
125
80
|
/**
|
|
126
81
|
* Comprehensive {@link TemperatureResult} as produced by
|
|
127
82
|
* {@link temperature}. Also see {@link ColorAnalysisOpts.minSat} and
|
|
@@ -133,28 +88,60 @@ export interface ColorAnalysisResult {
|
|
|
133
88
|
* {@link ColorAnalysisResult.lumaRange}).
|
|
134
89
|
*/
|
|
135
90
|
contrast: number;
|
|
136
|
-
/**
|
|
137
|
-
* Luminance contrast of entire grayscale image (i.e. delta of
|
|
138
|
-
* {@link ColorAnalysisResult.lumaRangeImg}).
|
|
139
|
-
*/
|
|
140
|
-
contrastImg: number;
|
|
141
91
|
/**
|
|
142
92
|
* Max. normalized WCAG color contrast of dominant colors. The original WCAG
|
|
143
93
|
* result range [1,21] is rescaled to [0,1].
|
|
144
94
|
*/
|
|
145
95
|
colorContrast: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Result data structure returned by {@link analyzeColors}.
|
|
99
|
+
*/
|
|
100
|
+
export interface ColorAnalysisResult extends BaseColorAnalysisResult {
|
|
101
|
+
/**
|
|
102
|
+
* Input image, possibly converted to float RGBA & resized.
|
|
103
|
+
*/
|
|
104
|
+
img: FloatBuffer;
|
|
105
|
+
/**
|
|
106
|
+
* Float grayscale version of {@link ColorAnalysisResult.img}.
|
|
107
|
+
*/
|
|
108
|
+
imgGray: FloatBuffer;
|
|
146
109
|
/**
|
|
147
|
-
*
|
|
110
|
+
* Float HSV version of {@link ColorAnalysisResult.img}.
|
|
148
111
|
*/
|
|
149
|
-
|
|
112
|
+
imgHsv: FloatBuffer;
|
|
150
113
|
/**
|
|
151
|
-
*
|
|
114
|
+
* Min/max luminance range of entire grayscale image (obtained from SRGB)
|
|
152
115
|
*/
|
|
153
|
-
|
|
116
|
+
lumImg: Metric;
|
|
154
117
|
/**
|
|
155
|
-
*
|
|
118
|
+
* Luminance contrast of entire grayscale image (i.e. delta of
|
|
119
|
+
* {@link ColorAnalysisResult.lumaRangeImg}).
|
|
156
120
|
*/
|
|
157
|
-
|
|
121
|
+
contrastImg: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Aggregated version of {@link BaseColorAnalysisResult}. Return type of
|
|
125
|
+
* {@link aggregateColorResults}.
|
|
126
|
+
*/
|
|
127
|
+
export interface AggregatedColorAnalysisResult {
|
|
128
|
+
css: string[];
|
|
129
|
+
srgb: SRGB[];
|
|
130
|
+
hsv: HSV[];
|
|
131
|
+
oklch: Oklch[];
|
|
132
|
+
hue: Metric;
|
|
133
|
+
sat: WeightedMetric;
|
|
134
|
+
chroma: WeightedMetric;
|
|
135
|
+
lum: WeightedMetric;
|
|
136
|
+
lumImg: Metric;
|
|
137
|
+
contrast: Metric;
|
|
138
|
+
contrastImg: Metric;
|
|
139
|
+
colorContrast: Metric;
|
|
140
|
+
temperature: {
|
|
141
|
+
meanHue: Metric;
|
|
142
|
+
temp: Metric;
|
|
143
|
+
areaTemp: Metric;
|
|
144
|
+
};
|
|
158
145
|
}
|
|
159
146
|
/**
|
|
160
147
|
* Result type for {@link temparature}.
|
package/hues.d.ts
CHANGED
|
@@ -87,15 +87,14 @@ export declare const hueRangeAreaIntensity: (colors: Iterable<ReadonlyVec>, hueR
|
|
|
87
87
|
*/
|
|
88
88
|
export declare const meanIntensity: (colors: Iterable<ReadonlyVec>) => number;
|
|
89
89
|
/**
|
|
90
|
-
* Constructs
|
|
90
|
+
* Constructs a min/max range of given normalized `hues` around given `mean` hue
|
|
91
91
|
* (also normalized), considering circular wrap-around. If `min > max` in the
|
|
92
|
-
* result `[min,max]`, then the hue range is crossing
|
|
92
|
+
* result `[min,max]`, then the hue range is crossing zero.
|
|
93
93
|
*
|
|
94
94
|
* @param hues
|
|
95
|
-
* @param range
|
|
96
95
|
* @param mean
|
|
97
96
|
*/
|
|
98
|
-
export declare const
|
|
97
|
+
export declare const hueRange: (values: import("@thi.ng/api").NumericArray, mean: number) => import("@thi.ng/metrics").Range;
|
|
99
98
|
/**
|
|
100
99
|
* Computes an abstract measure of a normalized "color temperature" of the given
|
|
101
100
|
* HSV `colors` (also normalized in [0,1] range). Results closer to 1.0 indicate
|
package/hues.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { ensureArray } from "@thi.ng/arrays/ensure-array";
|
|
2
2
|
import { compareByKey } from "@thi.ng/compare";
|
|
3
3
|
import { TAU } from "@thi.ng/math/api";
|
|
4
|
+
import { roundTo } from "@thi.ng/math/prec";
|
|
5
|
+
import { smoothStep } from "@thi.ng/math/step";
|
|
6
|
+
import { circularRange } from "@thi.ng/metrics/metrics";
|
|
4
7
|
import { normFrequenciesAuto, repeat, transduce } from "@thi.ng/transducers";
|
|
5
8
|
import { map } from "@thi.ng/transducers/map";
|
|
6
9
|
import { mapcat } from "@thi.ng/transducers/mapcat";
|
|
7
10
|
import { mean } from "@thi.ng/transducers/mean";
|
|
8
|
-
import { minMax } from "@thi.ng/transducers/min-max";
|
|
9
|
-
import { reduce } from "@thi.ng/transducers/reduce";
|
|
10
11
|
import { circularMean } from "@thi.ng/vectors/circular";
|
|
11
|
-
import { fract, roundTo } from "@thi.ng/math/prec";
|
|
12
|
-
import { smoothStep } from "@thi.ng/math/step";
|
|
13
12
|
function* selectHueRange(colors, minHue, maxHue, minSat) {
|
|
14
13
|
const pred = __hueSelector(minHue, maxHue);
|
|
15
14
|
for (let col of colors) {
|
|
@@ -64,29 +63,7 @@ const meanIntensity = (colors) => transduce(
|
|
|
64
63
|
mean(),
|
|
65
64
|
colors
|
|
66
65
|
);
|
|
67
|
-
const
|
|
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
|
-
};
|
|
66
|
+
const hueRange = circularRange;
|
|
90
67
|
const temperature = (colors, minSat = 0.2, coeffs) => {
|
|
91
68
|
const $colors = ensureArray(colors);
|
|
92
69
|
const filtered = $colors.filter((x) => x[1] >= minSat);
|
|
@@ -121,8 +98,8 @@ const DEFAULT_TEMPERATURE_COEFFS = [
|
|
|
121
98
|
const hueTemperature = (hue, [a, b, c, d] = DEFAULT_TEMPERATURE_COEFFS) => 2 * (hue < 2 / 3 ? smoothStep(b, a, hue) : smoothStep(c, d, hue)) - 1;
|
|
122
99
|
export {
|
|
123
100
|
DEFAULT_TEMPERATURE_COEFFS,
|
|
124
|
-
computeHueRange,
|
|
125
101
|
countHueRange,
|
|
102
|
+
hueRange,
|
|
126
103
|
hueRangeArea,
|
|
127
104
|
hueRangeAreaIntensity,
|
|
128
105
|
hueTemperature,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/pixel-analysis",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Image color & feature analysis utilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -39,16 +39,17 @@
|
|
|
39
39
|
"tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@thi.ng/api": "^8.11.
|
|
43
|
-
"@thi.ng/arrays": "^2.13.
|
|
44
|
-
"@thi.ng/color": "^5.7.
|
|
45
|
-
"@thi.ng/compare": "^2.4.
|
|
46
|
-
"@thi.ng/math": "^5.11.
|
|
47
|
-
"@thi.ng/
|
|
48
|
-
"@thi.ng/pixel
|
|
49
|
-
"@thi.ng/pixel-
|
|
50
|
-
"@thi.ng/
|
|
51
|
-
"@thi.ng/
|
|
42
|
+
"@thi.ng/api": "^8.11.33",
|
|
43
|
+
"@thi.ng/arrays": "^2.13.6",
|
|
44
|
+
"@thi.ng/color": "^5.7.54",
|
|
45
|
+
"@thi.ng/compare": "^2.4.25",
|
|
46
|
+
"@thi.ng/math": "^5.11.33",
|
|
47
|
+
"@thi.ng/metrics": "^0.2.0",
|
|
48
|
+
"@thi.ng/pixel": "^7.5.5",
|
|
49
|
+
"@thi.ng/pixel-convolve": "^1.1.5",
|
|
50
|
+
"@thi.ng/pixel-dominant-colors": "^2.0.9",
|
|
51
|
+
"@thi.ng/transducers": "^9.6.4",
|
|
52
|
+
"@thi.ng/vectors": "^8.6.1"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"esbuild": "^0.25.8",
|
|
@@ -112,5 +113,5 @@
|
|
|
112
113
|
"status": "beta",
|
|
113
114
|
"year": 2024
|
|
114
115
|
},
|
|
115
|
-
"gitHead": "
|
|
116
|
+
"gitHead": "76ffdc6e27bced158471ed47700919e7079956ca\n"
|
|
116
117
|
}
|