Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/Vibrant-Colors/node-vibrant

🎨 Extract prominent colors from an image
https://github.com/Vibrant-Colors/node-vibrant

Last synced: about 2 months ago
JSON representation

🎨 Extract prominent colors from an image

Awesome Lists containing this project

README

        

# node-vibrant
[![Build Status](https://travis-ci.org/akfish/node-vibrant.svg?branch=master)](https://travis-ci.org/akfish/node-vibrant)

Extract prominent colors from an image.

## ** NOTICE **

This project is refactored into a monorepo in version 3.2.0 (see [develop](https://github.com/akfish/node-vibrant/tree/develop) branch, npm version `[email protected]`).

We will not merge new PRs to v3.1 related to new featuresets during this time. However, bug fixes and security vulnerability fixes are still highly encouraged.

## New WebWorker support in v3.0

Quantization is the most time-consuming stage in `node-vibrant`. In v3.0, the quantization can be run in the WebWorker to avoid freezing the UI thread.

Here's how to use this feature:
1. Use WebWorker build `dist/vibrant.worker.js` or `dist/vibrant.worker.min.js`. Or if you are re-bundling with webpack, use `lib/bundle.worker.js` as entry
2. Use WebWorker quantizer:
```ts
let v = Vibrant.from(src)
.useQuantizer(Vibrant.Quantizer.WebWorker)
// Other configurations
```

## Features
- Identical API for both node.js and browser environment
- Support browserify/webpack
- Consistent results (*See [Result Consistency](#result-consistency))

## Install

```bash
$ npm install node-vibrant
```

## Usage
### node.js / browserify

```js
// ES5
var Vibrant = require('node-vibrant')
// ES6
import * as Vibrant from 'node-vibrant'
// TypeScript
import Vibrant = require('node-vibrant')

// Using builder
Vibrant.from('path/to/image').getPalette((err, palette) => console.log(palette))
// Promise
Vibrant.from('path/to/image').getPalette()
.then((palette) => console.log(palette))

// Using constructor
let v = new Vibrant('path/to/image', opts)
v.getPalette((err, palette) => console.log(palette))
// Promise
v.getPalette().then((palette) => console.log(palette))
```

### Browser

If you installed node-vibrant with `npm`, compiled bundles are available under `node_modules/node-vibrant/dist`.
Or you can download bundles from [Relases](https://github.com/akfish/node-vibrant/releases).

```html

// Use `Vibrant` in script
// Vibrant is exported to global. window.Vibrant === Vibrant
Vibrant.from('path/to/image').getPalette(function(err, palette) {});
// Promise
Vibrant.from('path/to/image').getPalette().then(function(palette) {});
// Or
var v = new Vibrant('/path/to/image', opts);
// ... same as in node.js

```

## Contribution Guidelines
1. Make changes
2. Write test specs if necessary
3. Pass tests
4. Commit **source files only** (without compiled files)

## References

### `Vibrant`
Main class of `node-vibrant`.

#### `Vibrant.from(src: ImageSource): Builder`
Make a `Builder` for an image. Returns a `Builder` instance.

#### `constructor(src: ImageSource, opts: Partial)`

Name | Description
------- | ---------------------------------------
`image` | Path to image file (support HTTP/HTTPs)
`opts` | Options (optional)

##### `ImageSource`

```ts
export type ImageSource = string
| HTMLImageElement // Browser only
| Buffer // Node.js only
```

##### `Options`

```ts
export interface Options {
colorCount: number
quality: number
maxDimension: number
filters: Array
ImageClass: ImageClass
quantizer: Quantizer
generator?: Generator
}
```

Field | Default | Description
-------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------
`colorCount` | 64 | amount of colors in initial palette from which the swatches will be generated
`quality` | 5 | Scale down factor used in downsampling stage. `1` means no downsampling. If `maxDimension` is set, this value will not be used.
`maxDimension` | `undefined` | The max size of the image's longer side used in downsampling stage. This field will override `quality`.
`filters` | `[]` | An array of filters
`ImageClass` | `Image.Node` or `Image.Browser` | An `Image` implementation class
`quantizer` | `Vibrant.Quantizer.MMCQ` | A `Quantizer` implementation class
`generator` | `Vibrant.Generator.Default` | An `Generator` instance

##### `Resolvable`

```ts
export type Resolvable = T | Promise
```

##### `Quantizer`

```ts
export interface Quantizer {
(pixels: Pixels, opts: Options): Resolvable>
}
```

##### `Generator`

```ts
export interface Generator {
(swatches: Array, opts?: Object): Resolvable
}
```

##### `Filter`

Returns `true` if the color is to be kept.

```ts
export interface Filter {
(red: number, green: number, blue: number, alpha: number): boolean
}
```

#### `getPalette(cb?: Callback): Promise`

Name | Description
---- | -----------------
`cb` | (Optional) callback function. Can be omitted when using `Promise`.

##### `Callback`

```ts
export interface Callback {
(err?: Error, result?: T): void
}
```

#### `getSwatches(cb?: Callback): Promise`
Alias of `getPalette`.

### `Vibrant.Builder`
Helper class for change configurations and create a `Vibrant` instance. Methods of a `Builder` instance can be chained like:

```ts
Vibrant.from(src)
.quality(1)
.clearFilters()
// ...
.getPalette()
.then((palette) => {})
```

#### `constructor(src: ImageSource, opts: Partial)`
Arguments are the same as `Vibrant.constructor`.

#### `quality(q: number): Builder`
Sets `opts.quality` to `q`. Returns this `Builder` instance.

#### `maxColorCount(n: number): Builder`
Sets `opts.colorCount` to `n`. Returns this `Builder` instance.

#### `maxDimension(d: number): Builder`
Sets `opts.maxDimension` to `d`. Returns this `Builder` instance.

#### `addFilter(f: Filter): Builder`
Adds a filter function. Returns this `Builder` instance.

#### `removeFilter(f: Filter): Builder`
Removes a filter function. Returns this `Builder` instance.

#### `clearFilters(): Builder`
Clear all filters. Returns this `Builder` instance.

#### `useImageClass(imageClass: ImageClass): Builder`
Specifies which `Image` implementation class to use. Returns this `Builder` instance.

#### `useQuantizer(quantizer: Quantizer): Builder`
Specifies which `Quantizer` implementation class to use. Returns this `Builder` instance.

#### `useGenerator(generator: Generator): Builder`
Sets `opts.generator` to `generator`. Returns this `Builder` instance.

#### `build(): Vibrant`
Builds and returns a `Vibrant` instance as configured.

#### `getPalette(cb?: Callback): Promise`
Builds a `Vibrant` instance as configured and calls its `getPalette` method.

#### `getSwatches(cb? Callback): Promise`
Alias of `getPalette`.

### `Vibrant.Swatch`
Represents a color swatch generated from an image's palette.

#### `Vec3`

```ts
export interface Vec3 extends Array {
0: number,
1: number,
2: number
}
```

#### `constructor(rgb: Vec3, population: number)`
Internal use.

Name | Description
------------ | -----------------------------------
`rgb` | `[r, g, b]`
`population` | Population of the color in an image

#### `getHsl(): Vec3`
#### `getPopulation(): number`
#### `getRgb(): Vec3`
#### `getHex(): string`
#### `getTitleTextColor(): string`
Returns an appropriate color to use for any 'title' text which is displayed over this `Swatch`'s color.

#### `getBodyTextColor(): string`
Returns an appropriate color to use for any 'body' text which is displayed over this `Swatch`'s color.

### `Vibrant.Util`
Utility methods. Internal usage.

#### `hexToRgb(hex: string): Vec3`
#### `rgbToHex(r: number, g: number, b: number): string`
#### `hslToRgb(h: number, s: number, l: number): Vec3`
#### `rgbToHsl(r: number, g: number, b: number): Vec3`
#### `xyzToRgb(x: number, y: number, z: number): Vec3`
#### `rgbToXyz(r: number, g: number, b: number): Vec3`
#### `xyzToCIELab(x: number, y: number, z: number): Vec3`
#### `rgbToCIELab(l: number, a: number, b: number): Vec3`
#### `deltaE94(lab1: number, lab2: number): number`
Computes CIE delta E 1994 diff between `lab1` and `lab2`. The 2 colors are in CIE-Lab color space. Used in tests to compare 2 colors' perceptual similarity.

#### `rgbDiff(rgb1: Vec3, rgb2: Vec3): number`
Compute CIE delta E 1994 diff between `rgb1` and `rgb2`.

#### `hexDiff(hex1: string, hex2: string): number`
Compute CIE delta E 1994 diff between `hex1` and `hex2`.

#### `getColorDiffStatus(d: number): string`
Gets a string to describe the meaning of the color diff. Used in tests.

Delta E | Perception | Returns
-------- | -------------------------------------- | -----------
<= 1.0 | Not perceptible by human eyes. | `"Perfect"`
1 - 2 | Perceptible through close observation. | `"Close"`
2 - 10 | Perceptible at a glance. | `"Good"`
11 - 49 | Colors are more similar than opposite | `"Similar"`
50 - 100 | Colors are exact opposite | `Wrong`

## NPM Tasks

Task | Description
--------------- | --------------------------------------
`build:browser` | Build browser target
`build:node` | Build node.js target
`build` | Build all targets
`clean:browser` | Clean browser build
`clean:node` | Clean node.js build
`clean` | Clean all builds
`test:browser` | Run browser specs (karma)
`test:node` | Run node.js specs (mocha)
`test` | Run all specs

## Notes
### Intentional Deviation From `vibrant.js`
- `node-vibrant` takes image path, not the image object as parameter for the obvious reason that node.js environment has no access to HTML DOM object.
- `node-vibrant` provides asynchronous API since most node.js image processing library is asynchronous. And the original `vibrant.js` workflow is asynchronous any way (though you will have to handle the image loading yourself, while `node-vibrant` does it for you).
- `node-vibrant` uses one single `opts` object to hold all options for future expansions. And it feels more node.js-like.
- `node-vibrant` uses method call to initiate image processing instead of constructor so that developers can use it with `Promise`.

### Result Consistency
The results is consistent within each user's browser instance regardelss of visible region or display size of the image, unlike the original `vibrant.js` implementation.

However, due to the very nature of HTML5 canvas element, image rendering is platform/machine-dependent. Thus the resulting swatches in browser environment varies and may not be the same as in node.js nor in another machine. See [Canvas Fingerprinting](https://en.wikipedia.org/wiki/Canvas_fingerprinting).

The test specs use CIE delta E 1994 color difference to measure inconsistencies across platforms. It compares the generated color on node.js, Chrome, Firefox and IE11. At `quality` == 1 (no downsampling) and no filters, the results are rather consistent. Color diffs between browsers are mostly not perceptible by human eyes. Downsampling _will_ cause perceptible inconsistent results across browsers due to differences in canvas implementations.