@thi.ng/pixel-dominant-colors 1.1.49 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-06-09T17:24:08Z
3
+ - **Last updated**: 2025-07-10T14:20:23Z
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,20 @@ 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-dominant-colors@2.0.0) (2025-06-24)
15
+
16
+ #### 🛑 Breaking changes
17
+
18
+ - update dominantColors() ([9cf7a48](https://github.com/thi-ng/umbrella/commit/9cf7a48))
19
+ - BREAKING CHANGE: rename `dominantColors()` => `dominantColorsKmeans()`, remove `dominantColorsArray()`
20
+ - merge `dominantColors()` & `dominantColorsArray()` => `dominantColorsKmeans()`
21
+ - add `DominantColor` result type
22
+ - add filterSamples helper
23
+
24
+ #### 🚀 Features
25
+
26
+ - add dominantColorsMeanCut()/dominantColorsMedianCut() ([2ab48c7](https://github.com/thi-ng/umbrella/commit/2ab48c7))
27
+
14
28
  ### [1.1.2](https://github.com/thi-ng/umbrella/tree/@thi.ng/pixel-dominant-colors@1.1.2) (2024-07-25)
15
29
 
16
30
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -33,15 +33,18 @@ extracted from the [@thi.ng/pixel](https://thi.ng/pixel) package.
33
33
 
34
34
  ### Dominant color extraction
35
35
 
36
- The
37
- [`dominantColors()`](https://docs.thi.ng/umbrella/pixel/functions/dominantColors.html)
38
- function applies [k-means
39
- clustering](https://github.com/thi-ng/umbrella/tree/develop/packages/k-means) to
40
- extract the dominant colors from the given image. The clustering can be
41
- configured. The function returns an array of `{ color, area }` objects (sorted
42
- by descending area), where `color` is a cluster's dominant color (in the format
43
- of the source image) and `area` the normalized cluster size (number of selected
44
- pixels over total number of pixels in the image).
36
+ The package provides several methods to extract the dominant colors from a given image:
37
+
38
+ - [`dominantColorsKmeans()`](https://docs.thi.ng/umbrella/pixel/functions/dominantColorsKmeans.html)
39
+ uses [k-means clustering](https://github.com/thi-ng/umbrella/tree/develop/packages/k-means)
40
+ - [`dominantColorsMeanCut()`](https://docs.thi.ng/umbrella/pixel/functions/dominantColorsMeanCut.html)
41
+ - [`dominantColorsMedianCut()`](https://docs.thi.ng/umbrella/pixel/functions/dominantColorsMedianCut.html)
42
+
43
+ In all cases the clustering can be configured. The functions return an array of
44
+ `{ color, area }` objects (sorted by descending area), where `color` is a
45
+ cluster's dominant color (in the format of the source image) and `area` the
46
+ normalized cluster size (number of selected pixels over total number of pixels
47
+ in the image).
45
48
 
46
49
  Also see the [dominant colors example project & online
47
50
  tool](https://demo.thi.ng/umbrella/dominant-colors/) based on this function.
@@ -123,13 +126,14 @@ For Node.js REPL:
123
126
  const pdc = await import("@thi.ng/pixel-dominant-colors");
124
127
  ```
125
128
 
126
- Package sizes (brotli'd, pre-treeshake): ESM: 223 bytes
129
+ Package sizes (brotli'd, pre-treeshake): ESM: 381 bytes
127
130
 
128
131
  ## Dependencies
129
132
 
130
133
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
131
134
  - [@thi.ng/k-means](https://github.com/thi-ng/umbrella/tree/develop/packages/k-means)
132
135
  - [@thi.ng/pixel](https://github.com/thi-ng/umbrella/tree/develop/packages/pixel)
136
+ - [@thi.ng/vectors](https://github.com/thi-ng/umbrella/tree/develop/packages/vectors)
133
137
 
134
138
  Note: @thi.ng/api is in _most_ cases a type-only import (not used at runtime)
135
139
 
package/api.d.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { Fn2, NumericArray } from "@thi.ng/api";
2
+ /**
3
+ * Options for {@link dominantColors}, an extension of
4
+ * [KMeansOpts](https://docs.thi.ng/umbrella/k-means/interfaces/KMeansOpts.html).
5
+ */
6
+ export interface DominantColorOpts {
7
+ /**
8
+ * Predicate used to only include pixels in the analysis for which the
9
+ * filter returns truthy result. E.g. to pre-exclude weakly saturated or
10
+ * dark colors etc. The second arg is the index of the pixel in the image's
11
+ * pixel buffer.
12
+ *
13
+ * If omitted, all pixels will be included (default).
14
+ */
15
+ filter: Fn2<NumericArray, number, boolean>;
16
+ }
17
+ /**
18
+ * Result type for dominant color extraction functions
19
+ */
20
+ export interface DominantColor {
21
+ /**
22
+ * RGB tuple
23
+ */
24
+ color: number[];
25
+ /**
26
+ * Normalized area (based on number of samples)
27
+ */
28
+ area: number;
29
+ /**
30
+ * Sample IDs (indices) belonging to this cluster.
31
+ */
32
+ items?: number[];
33
+ }
34
+ //# sourceMappingURL=api.d.ts.map
package/api.js ADDED
File without changes
package/index.d.ts CHANGED
@@ -1,53 +1,4 @@
1
- import type { Fn2, NumericArray } from "@thi.ng/api";
2
- import type { KMeansOpts } from "@thi.ng/k-means";
3
- import type { FloatBuffer } from "@thi.ng/pixel/float";
4
- /**
5
- * Options for {@link dominantColors}, an extension of
6
- * [KMeansOpts](https://docs.thi.ng/umbrella/k-means/interfaces/KMeansOpts.html).
7
- */
8
- export interface DominantColorOpts extends KMeansOpts {
9
- /**
10
- * Predicate used to only include pixels in the analysis for which the
11
- * filter returns truthy result. E.g. to pre-exclude weakly saturated or
12
- * dark colors etc. The second arg is the index of the pixel in the image's
13
- * pixel buffer.
14
- *
15
- * If omitted, all pixels will be included (default).
16
- */
17
- filter: Fn2<Float32Array, number, boolean>;
18
- }
19
- /**
20
- * Takes a {@link FloatBuffer} and applies k-means clustering to extract the
21
- * `num` dominant colors from the given image. The clustering can be configured
22
- * via optionally provided `opts`. Returns array of `{ color, area }` objects
23
- * (sorted by descending area), where `color` is a cluster's dominant color and
24
- * `area` the normalized cluster size.
25
- *
26
- * @remarks
27
- * This function is syntax sugar for {@link dominantColorsArray}.
28
- *
29
- * See thi.ng/k-means for details about clustering implementation & options.
30
- *
31
- * @param img -
32
- * @param num -
33
- * @param opts -
34
- */
35
- export declare const dominantColors: (img: FloatBuffer, num: number, opts?: Partial<DominantColorOpts>) => {
36
- color: number[];
37
- area: number;
38
- ids: number[];
39
- }[];
40
- /**
41
- * Similar to {@link dominantColors}, but accepting an array of color samples
42
- * instead of a `FloatBuffer` image.
43
- *
44
- * @param num
45
- * @param samples
46
- * @param opts
47
- */
48
- export declare const dominantColorsArray: (num: number, samples: NumericArray[], opts?: Partial<KMeansOpts>) => {
49
- color: number[];
50
- area: number;
51
- ids: number[];
52
- }[];
1
+ export * from "./api.js";
2
+ export * from "./kmeans.js";
3
+ export * from "./mean-cut.js";
53
4
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,20 +1,3 @@
1
- import { kmeans } from "@thi.ng/k-means/kmeans";
2
- const dominantColors = (img, num, opts) => {
3
- const samples = [];
4
- const filter = opts?.filter || (() => true);
5
- let i = 0;
6
- for (let p of img) {
7
- if (filter(p, i)) samples.push(p);
8
- i++;
9
- }
10
- return samples.length ? dominantColorsArray(num, samples, opts) : [];
11
- };
12
- const dominantColorsArray = (num, samples, opts) => kmeans(Math.min(num, samples.length), samples, opts).sort((a, b) => b.items.length - a.items.length).map((c) => ({
13
- color: [...c.centroid],
14
- area: c.items.length / samples.length,
15
- ids: c.items
16
- }));
17
- export {
18
- dominantColors,
19
- dominantColorsArray
20
- };
1
+ export * from "./api.js";
2
+ export * from "./kmeans.js";
3
+ export * from "./mean-cut.js";
package/kmeans.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { NumericArray } from "@thi.ng/api";
2
+ import type { KMeansOpts } from "@thi.ng/k-means";
3
+ import type { FloatBuffer } from "@thi.ng/pixel/float";
4
+ import type { DominantColor, DominantColorOpts } from "./api.js";
5
+ /**
6
+ * Takes a {@link FloatBuffer} and applies k-means clustering to extract the
7
+ * `num` dominant colors from the given image. The clustering can be configured
8
+ * via optionally provided `opts`. Returns array of `{ color, area }` objects
9
+ * (sorted by descending area), where `color` is a cluster's dominant color and
10
+ * `area` the normalized cluster size.
11
+ *
12
+ * @remarks
13
+ * See thi.ng/k-means for details about clustering implementation & options.
14
+ *
15
+ * @param img -
16
+ * @param num -
17
+ * @param opts -
18
+ */
19
+ export declare const dominantColorsKmeans: (img: FloatBuffer | NumericArray[], num: number, opts?: Partial<DominantColorOpts & KMeansOpts>) => DominantColor[];
20
+ //# sourceMappingURL=kmeans.d.ts.map
package/kmeans.js ADDED
@@ -0,0 +1,15 @@
1
+ import { kmeans } from "@thi.ng/k-means/kmeans";
2
+ import { filterSamples } from "./utils.js";
3
+ const dominantColorsKmeans = (img, num, opts) => {
4
+ const samples = opts?.filter ? filterSamples(opts.filter, img) : Array.isArray(img) ? img : [...img];
5
+ return samples.length ? kmeans(Math.min(num, samples.length), samples, opts).sort((a, b) => b.items.length - a.items.length).map(
6
+ (c) => ({
7
+ color: [...c.centroid],
8
+ area: c.items.length / samples.length,
9
+ ids: c.items
10
+ })
11
+ ) : [];
12
+ };
13
+ export {
14
+ dominantColorsKmeans
15
+ };
package/mean-cut.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { NumericArray } from "@thi.ng/api";
2
+ import type { FloatBuffer } from "@thi.ng/pixel/float";
3
+ import type { DominantColor, DominantColorOpts } from "./api.js";
4
+ export declare const dominantColorsMeanCut: (img: FloatBuffer | NumericArray[], num: number, opts?: Partial<DominantColorOpts>) => DominantColor[];
5
+ export declare const dominantColorsMedianCut: (img: FloatBuffer | NumericArray[], num: number, opts?: Partial<DominantColorOpts>) => DominantColor[];
6
+ //# sourceMappingURL=mean-cut.d.ts.map
package/mean-cut.js ADDED
@@ -0,0 +1,19 @@
1
+ import { computeCutWith } from "@thi.ng/k-means/mean-cut";
2
+ import { mean, vmean } from "@thi.ng/vectors/mean";
3
+ import { vmedian } from "@thi.ng/vectors/median";
4
+ import { filterSamples } from "./utils.js";
5
+ const dominantColorsMeanCut = (img, num, opts) => __dominantColors(vmean, img, num, opts);
6
+ const dominantColorsMedianCut = (img, num, opts) => __dominantColors(vmedian, img, num, opts);
7
+ const __dominantColors = (cut, img, num, opts) => {
8
+ const samples = opts?.filter ? filterSamples(opts.filter, img) : [...img];
9
+ return samples.length ? computeCutWith(cut, samples, samples[0].length, num).sort((a, b) => b.length - a.length).map(
10
+ (bin) => ({
11
+ color: mean([], bin),
12
+ area: bin.length / samples.length
13
+ })
14
+ ) : [];
15
+ };
16
+ export {
17
+ dominantColorsMeanCut,
18
+ dominantColorsMedianCut
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/pixel-dominant-colors",
3
- "version": "1.1.49",
3
+ "version": "2.0.1",
4
4
  "description": "k-means based dominant color extraction from images/pixel buffers",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -39,13 +39,14 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.11.29",
43
- "@thi.ng/k-means": "^1.1.1",
44
- "@thi.ng/pixel": "^7.5.1"
42
+ "@thi.ng/api": "^8.11.30",
43
+ "@thi.ng/k-means": "^2.0.1",
44
+ "@thi.ng/pixel": "^7.5.2",
45
+ "@thi.ng/vectors": "^8.3.2"
45
46
  },
46
47
  "devDependencies": {
47
- "esbuild": "^0.25.5",
48
- "typedoc": "^0.28.5",
48
+ "esbuild": "^0.25.6",
49
+ "typedoc": "^0.28.7",
49
50
  "typescript": "^5.8.3"
50
51
  },
51
52
  "keywords": [
@@ -55,7 +56,10 @@
55
56
  "dominant",
56
57
  "image",
57
58
  "k-means",
59
+ "mean",
60
+ "median",
58
61
  "palette",
62
+ "quantize",
59
63
  "typescript"
60
64
  ],
61
65
  "publishConfig": {
@@ -75,6 +79,18 @@
75
79
  "exports": {
76
80
  ".": {
77
81
  "default": "./index.js"
82
+ },
83
+ "./api": {
84
+ "default": "./api.js"
85
+ },
86
+ "./kmeans": {
87
+ "default": "./kmeans.js"
88
+ },
89
+ "./mean-cut": {
90
+ "default": "./mean-cut.js"
91
+ },
92
+ "./utils": {
93
+ "default": "./utils.js"
78
94
  }
79
95
  },
80
96
  "thi.ng": {
@@ -82,5 +98,5 @@
82
98
  "year": 2021,
83
99
  "screenshot": "pixel/dominant-colors-01.jpg"
84
100
  },
85
- "gitHead": "b076434a497b291ad33e81b1a15f6a71e2c82cc2\n"
101
+ "gitHead": "56d8f088389b22192a06e9a395b5eecebf47697a\n"
86
102
  }
package/utils.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { Fn2, NumericArray } from "@thi.ng/api";
2
+ /** @internal */
3
+ export declare const filterSamples: (pred: Fn2<NumericArray, number, boolean>, img: Iterable<NumericArray>) => NumericArray[];
4
+ //# sourceMappingURL=utils.d.ts.map
package/utils.js ADDED
@@ -0,0 +1,12 @@
1
+ const filterSamples = (pred, img) => {
2
+ const samples = [];
3
+ let i = 0;
4
+ for (let p of img) {
5
+ if (pred(p, i)) samples.push(p);
6
+ i++;
7
+ }
8
+ return samples;
9
+ };
10
+ export {
11
+ filterSamples
12
+ };