Generate an array of colours from an image based on k-means clustering.
Version 3.0 includes a lot of changes to optimization and performance.
Here's an example of usage in a React component
import {
type Color,
ImageKmeans,
type RunResult,
default as init,
InitMethod,
} from "@zyzle/image-kmeans";
function MyComponent() {
const [wasmInstance, setWasmInstance] = useState<ImageKmeans | null>(null);
useEffect(() => {
init();
}, []);
// Drop handler
const handleDroppedFile = useCallback(
async (ibm: ImageBitmap) => {
const canvas = new OffscreenCanvas(ibm.width, ibm.height);
const ctx = canvas.getContext(
"2d"
)! as unknown as CanvasRenderingContext2D;
ctx.canvas.height = ibm.height;
ctx.canvas.width = ibm.width;
ctx.drawImage(ibm, 0, 0);
const wasmInstance = new ImageKmeans(ctx, ibm.width, ibm.height);
setWasmInstance(wasmInstance);
},
[setWasmInstance]
);
}Use a pre-determined number of clusters k_number for the calculations.
with_fixed_k_number(k_number: number, init_method: InitMethod, config: Config): Promise<RunResult>;The module will do multiple runs of the k-means algorithm and determine the best fit for the number of selected clusters.
with_derived_k_number(init_method: InitMethod, config: Config): Promise<RunResult>;The InitMethod defines whether or not the initial clusters used in each run should be random or use the K-means++ algorithm.
The Config object used in both run types takes the following options:
| Option | Value |
|---|---|
quantize_fact |
clamp the colour space to given factor before calculating. Although this can significantly speed up performance it can result in the returned colors being close (usually indistinguishable at lower values) but not exactly those in the image |
top_num |
only consider the top n number of colours by frequency in calcultions |
Warning: not providing either a
quantize_factortop_numconfiguration will result in every color in the image being used in calculations, this can take significantly longer to process on larger images with lots of colors like photographs
Both of the above now return a RunResult object which looks like the following:
/**
* Each \'run\' of the cluster calculation produces a result
* containing the `k` size used, the vector of clusters found
* and the within-cluster sum of squares (WCSS)
*/
export interface RunResult {
/**
* The number of `k` clusters used for this run
*/
ks: number;
/**
* The cluster centroids found in this run
*/
clusters: Color[];
/**
* The within-cluster sum of squares for this run
*/
wcss: number;
}Due to changes in the getrandom crate you will either have to build with
RUSTFLAGS='--cfg getrandom_backend="wasm_js"'or update your cargo configuration as described in the getrandom docs
Use wasm-pack to build the Rust source into WebAssembly, this will output the JS/Wasm into a pkg folder using:
wasm-pack build --target web
# Or if you haven't made the updates to your cargo config
RUSTFLAGS='--cfg getrandom_backend="wasm_js"' wasm-pack build --target webLicensed under MIT license (LICENSE or http://opensource.org/licenses/MIT)