colorlip 0.1.0 → 1.0.0
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/LICENSE +5 -0
- package/README.ja.md +394 -0
- package/README.md +326 -60
- package/dist/adapters/canvas.cjs +571 -174
- package/dist/adapters/canvas.cjs.map +1 -1
- package/dist/adapters/canvas.d.cts +15 -5
- package/dist/adapters/canvas.d.ts +15 -5
- package/dist/adapters/canvas.js +31 -7
- package/dist/adapters/canvas.js.map +1 -1
- package/dist/adapters/chunk-RJEBQW5Y.js +696 -0
- package/dist/adapters/chunk-RJEBQW5Y.js.map +1 -0
- package/dist/adapters/core-RJ20zx2h.d.cts +107 -0
- package/dist/adapters/core-RJ20zx2h.d.ts +107 -0
- package/dist/adapters/sharp.cjs +568 -183
- package/dist/adapters/sharp.cjs.map +1 -1
- package/dist/adapters/sharp.d.cts +11 -6
- package/dist/adapters/sharp.d.ts +11 -6
- package/dist/adapters/sharp.js +30 -16
- package/dist/adapters/sharp.js.map +1 -1
- package/dist/index.cjs +541 -168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -25
- package/dist/index.d.ts +89 -25
- package/dist/index.js +539 -168
- package/dist/index.js.map +1 -1
- package/package.json +35 -6
- package/dist/adapters/chunk-5PQQ7VW4.js +0 -325
- package/dist/adapters/chunk-5PQQ7VW4.js.map +0 -1
- package/dist/adapters/core-OmHuknYv.d.cts +0 -42
- package/dist/adapters/core-OmHuknYv.d.ts +0 -42
package/LICENSE
CHANGED
|
@@ -19,3 +19,8 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Note: The `samples/` directory contains sample images that are NOT covered by
|
|
26
|
+
this MIT License. See `samples/LICENSE` for details.
|
package/README.ja.md
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
# colorlip
|
|
2
|
+
|
|
3
|
+
Node.js とブラウザ向けの、知覚寄りに調整した代表色・パレット抽出ライブラリです。
|
|
4
|
+
|
|
5
|
+
`colorlip` は、画像から代表色やコンパクトなパレットを抽出するための、軽量で高速な TypeScript ファーストのライブラリです。特にイラスト、アートワーク、商品画像のような「見た目の印象」が大事な画像で、単純に多い色ではなく、よりその画像らしく見える色を拾えるように調整しています。日本のイラスト系コミュニティや SNS、ショッピングサイトのようなビジュアル中心の用途でも使いやすいことを意識して設計しています。
|
|
6
|
+
|
|
7
|
+
コアはピクセルバッファを対象に動作して、ランタイム依存はありません。Node.js では `sharp`、ブラウザでは Canvas API を使うアダプターを利用できます。
|
|
8
|
+
|
|
9
|
+
[English README](./README.md)
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## 特徴
|
|
14
|
+
|
|
15
|
+
- イラストやアートワークでも扱いやすい、知覚寄りの色抽出
|
|
16
|
+
- 中央/外周やエッジ分布を活かす、構図寄りの重み付け
|
|
17
|
+
- 画像の傾向に合わせて調整される適応的なパレット抽出
|
|
18
|
+
- 近い色を CIELAB Delta E で自然にまとめるマージ処理
|
|
19
|
+
- `dominant`、`accent`、`swatches` を役割付きで返せるパレット API
|
|
20
|
+
- `hex`、`HSL`、`Lab`、`LCH`、`OKLab`、`OKLCH`、CSS カラー文字列、色相カテゴリを含む豊富な出力
|
|
21
|
+
- 生ピクセルからそのまま使える、プラットフォーム非依存のコア
|
|
22
|
+
- `sharp` と Canvas に対応したアダプターを同梱
|
|
23
|
+
- コアはランタイム依存なしで軽量
|
|
24
|
+
|
|
25
|
+
## colorlip らしさ
|
|
26
|
+
|
|
27
|
+
`colorlip` は、単に色を量子化して上位色を返すだけのライブラリではありません。
|
|
28
|
+
|
|
29
|
+
画像全体の色分布を要約するというより、軽量なヒューリスティックと知覚色空間を使って、「その画像で重要そうに見える色」「実際に使いやすいパレット」を選ぶ寄りの設計です。
|
|
30
|
+
|
|
31
|
+
- 画像ごとの統計から、彩度の閾値や重み付けを適応的に調整します
|
|
32
|
+
- 中央/外周やエッジ分布を見て、背景色ばかりが勝ちにくいようにしています
|
|
33
|
+
- Lab で近い色をまとめつつ、`dominant` と `accent` は別の役割で選びます
|
|
34
|
+
- イラスト、アートワーク、サムネイル、商品画像のような用途を強く意識しています
|
|
35
|
+
|
|
36
|
+
## インストール
|
|
37
|
+
|
|
38
|
+
コアのみを使う場合:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install colorlip
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Node.js で `sharp` アダプターを使う場合:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install colorlip sharp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`sharp` は optional peer dependency で、`colorlip/sharp` を使う場合のみ必要です。
|
|
51
|
+
|
|
52
|
+
## クイックスタート
|
|
53
|
+
|
|
54
|
+
### Node.js
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { getColors, getPalette } from "colorlip/sharp";
|
|
58
|
+
|
|
59
|
+
const colors = await getColors("photo.jpg");
|
|
60
|
+
const palette = await getPalette("photo.jpg");
|
|
61
|
+
|
|
62
|
+
console.log(colors[0]?.hex);
|
|
63
|
+
console.log(palette.dominant?.hex);
|
|
64
|
+
console.log(palette.accent?.hex);
|
|
65
|
+
console.log(palette.swatches);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### ブラウザ
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { getColors, getPalette } from "colorlip/canvas";
|
|
72
|
+
|
|
73
|
+
const colors = await getColors(imgElement);
|
|
74
|
+
const palette = await getPalette(imgElement);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 生ピクセル
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { getColors, getPalette } from "colorlip";
|
|
81
|
+
|
|
82
|
+
const colors = getColors(pixelData, width, height, channels);
|
|
83
|
+
const palette = getPalette(pixelData, width, height, channels);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## パッケージのエントリーポイント
|
|
87
|
+
|
|
88
|
+
### `colorlip`
|
|
89
|
+
|
|
90
|
+
生ピクセルバッファ向けのコア API です。
|
|
91
|
+
|
|
92
|
+
### `colorlip/sharp`
|
|
93
|
+
|
|
94
|
+
`sharp` ベースの Node.js アダプターです。画像を読み込み、縮小し、生ピクセルをコアに渡します。
|
|
95
|
+
|
|
96
|
+
### `colorlip/canvas`
|
|
97
|
+
|
|
98
|
+
Canvas API ベースのブラウザ用アダプターです。ブラウザの画像ソースを受け取り、Canvas に描画して `ImageData` をコアに渡します。
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
### コア
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import {
|
|
106
|
+
aggregateColors,
|
|
107
|
+
colorlip,
|
|
108
|
+
createDominantColor,
|
|
109
|
+
extractFallbackPalette,
|
|
110
|
+
getColors,
|
|
111
|
+
getHueCategory,
|
|
112
|
+
getPalette,
|
|
113
|
+
rgbToHex,
|
|
114
|
+
rgbToHsl,
|
|
115
|
+
} from "colorlip";
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `getColors(data, width, height, channels, options?)`
|
|
119
|
+
|
|
120
|
+
生のピクセルデータから代表色を抽出します。
|
|
121
|
+
|
|
122
|
+
- `data`: `Uint8Array | Uint8ClampedArray`
|
|
123
|
+
- `width`: 画像の幅
|
|
124
|
+
- `height`: 画像の高さ
|
|
125
|
+
- `channels`: 通常は `3` または `4`
|
|
126
|
+
|
|
127
|
+
戻り値は `ColorlipColor[]` です。
|
|
128
|
+
|
|
129
|
+
#### `getPalette(data, width, height, channels, options?)`
|
|
130
|
+
|
|
131
|
+
生のピクセルデータから構造化されたパレットを抽出します。
|
|
132
|
+
|
|
133
|
+
戻り値:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
interface ColorlipPalette {
|
|
137
|
+
dominant: ColorlipColor | null;
|
|
138
|
+
accent: ColorlipColor | null;
|
|
139
|
+
swatches: ColorlipColor[];
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `colorlip(...)`
|
|
144
|
+
|
|
145
|
+
`getColors(...)` の互換エイリアスです。
|
|
146
|
+
|
|
147
|
+
### Node.js アダプター
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import {
|
|
151
|
+
colorlip,
|
|
152
|
+
colorlipFromBuffer,
|
|
153
|
+
colorlipFromFile,
|
|
154
|
+
getColors,
|
|
155
|
+
getColorsFromPixels,
|
|
156
|
+
getPalette,
|
|
157
|
+
getPaletteFromPixels,
|
|
158
|
+
} from "colorlip/sharp";
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `getColors(source, options?)`
|
|
162
|
+
|
|
163
|
+
- `source`: `string | Buffer | Uint8Array`
|
|
164
|
+
|
|
165
|
+
`sharp` で画像を読み込み、`Promise<ColorlipColor[]>` を返します。
|
|
166
|
+
|
|
167
|
+
#### `getPalette(source, options?)`
|
|
168
|
+
|
|
169
|
+
- `source`: `string | Buffer | Uint8Array`
|
|
170
|
+
|
|
171
|
+
`sharp` で画像を読み込み、`Promise<ColorlipPalette>` を返します。
|
|
172
|
+
|
|
173
|
+
#### `getColorsFromPixels(...)`
|
|
174
|
+
|
|
175
|
+
生ピクセル向けコア API の再エクスポートです。
|
|
176
|
+
|
|
177
|
+
#### `getPaletteFromPixels(...)`
|
|
178
|
+
|
|
179
|
+
生ピクセル向けコアのパレット API の再エクスポートです。
|
|
180
|
+
|
|
181
|
+
#### 互換エイリアス
|
|
182
|
+
|
|
183
|
+
- `colorlipFromFile(...)`
|
|
184
|
+
- `colorlipFromBuffer(...)`
|
|
185
|
+
|
|
186
|
+
### ブラウザアダプター
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import {
|
|
190
|
+
colorlip,
|
|
191
|
+
colorlipFromImage,
|
|
192
|
+
colorlipFromImageData,
|
|
193
|
+
getColors,
|
|
194
|
+
getColorsFromImageData,
|
|
195
|
+
getColorsFromPixels,
|
|
196
|
+
getPalette,
|
|
197
|
+
getPaletteFromImageData,
|
|
198
|
+
getPaletteFromPixels,
|
|
199
|
+
} from "colorlip/canvas";
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### `getColors(source, options?)`
|
|
203
|
+
|
|
204
|
+
- `source`: `HTMLImageElement | ImageBitmap | Blob | string`
|
|
205
|
+
|
|
206
|
+
戻り値は `Promise<ColorlipColor[]>` です。
|
|
207
|
+
|
|
208
|
+
#### `getPalette(source, options?)`
|
|
209
|
+
|
|
210
|
+
- `source`: `HTMLImageElement | ImageBitmap | Blob | string`
|
|
211
|
+
|
|
212
|
+
戻り値は `Promise<ColorlipPalette>` です。
|
|
213
|
+
|
|
214
|
+
#### `getColorsFromImageData(imageData, options?)`
|
|
215
|
+
|
|
216
|
+
`ImageData` から直接色を抽出します。
|
|
217
|
+
|
|
218
|
+
#### `getPaletteFromImageData(imageData, options?)`
|
|
219
|
+
|
|
220
|
+
`ImageData` から直接パレットを抽出します。
|
|
221
|
+
|
|
222
|
+
#### `getColorsFromPixels(...)`
|
|
223
|
+
|
|
224
|
+
生ピクセル向けコア API の再エクスポートです。
|
|
225
|
+
|
|
226
|
+
#### `getPaletteFromPixels(...)`
|
|
227
|
+
|
|
228
|
+
生ピクセル向けコアのパレット API の再エクスポートです。
|
|
229
|
+
|
|
230
|
+
#### 互換エイリアス
|
|
231
|
+
|
|
232
|
+
- `colorlipFromImage(...)`
|
|
233
|
+
- `colorlipFromImageData(...)`
|
|
234
|
+
|
|
235
|
+
## オプション
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
interface ExtractOptions {
|
|
239
|
+
numColors?: number; // default: 3
|
|
240
|
+
saturationThreshold?: number; // default: 0.15
|
|
241
|
+
brightnessMin?: number; // default: 20
|
|
242
|
+
brightnessMax?: number; // default: 235
|
|
243
|
+
quantizationStep?: number; // default: 12
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
デフォルト値:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
{
|
|
251
|
+
numColors: 3,
|
|
252
|
+
saturationThreshold: 0.15,
|
|
253
|
+
brightnessMin: 20,
|
|
254
|
+
brightnessMax: 235,
|
|
255
|
+
quantizationStep: 12,
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## 出力
|
|
260
|
+
|
|
261
|
+
抽出された各色は `ColorlipColor` として返されます。
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
interface ColorlipColor {
|
|
265
|
+
r: number;
|
|
266
|
+
g: number;
|
|
267
|
+
b: number;
|
|
268
|
+
hex: string;
|
|
269
|
+
percentage: number;
|
|
270
|
+
hue: number;
|
|
271
|
+
saturation: number;
|
|
272
|
+
lightness: number;
|
|
273
|
+
hueCategory: HueCategory;
|
|
274
|
+
lab: { L: number; a: number; b: number };
|
|
275
|
+
lch: { L: number; C: number; H: number };
|
|
276
|
+
oklab: { L: number; a: number; b: number };
|
|
277
|
+
oklch: { L: number; C: number; H: number };
|
|
278
|
+
css: {
|
|
279
|
+
rgb: string;
|
|
280
|
+
hsl: string;
|
|
281
|
+
lab: string;
|
|
282
|
+
lch: string;
|
|
283
|
+
oklab: string;
|
|
284
|
+
oklch: string;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
例:
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
{
|
|
293
|
+
r: 42,
|
|
294
|
+
g: 98,
|
|
295
|
+
b: 168,
|
|
296
|
+
hex: "#2A62A8",
|
|
297
|
+
percentage: 0.34,
|
|
298
|
+
hue: 213,
|
|
299
|
+
saturation: 60,
|
|
300
|
+
lightness: 41,
|
|
301
|
+
hueCategory: "blue",
|
|
302
|
+
lab: { L: 41.2, a: -2.3, b: -40.1 },
|
|
303
|
+
lch: { L: 41.2, C: 40.2, H: 266.7 },
|
|
304
|
+
oklab: { L: 0.49, a: -0.03, b: -0.12 },
|
|
305
|
+
oklch: { L: 0.49, C: 0.12, H: 256.7 },
|
|
306
|
+
css: {
|
|
307
|
+
rgb: "rgb(42 98 168)",
|
|
308
|
+
hsl: "hsl(213 60% 41%)",
|
|
309
|
+
lab: "lab(41.2 -2.3 -40.1)",
|
|
310
|
+
lch: "lch(41.2 40.2 266.7)",
|
|
311
|
+
oklab: "oklab(0.49 -0.03 -0.12)",
|
|
312
|
+
oklch: "oklch(0.49 0.12 256.7)",
|
|
313
|
+
},
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## ユーティリティ関数
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import {
|
|
321
|
+
aggregateColors,
|
|
322
|
+
createDominantColor,
|
|
323
|
+
getHueCategory,
|
|
324
|
+
rgbToHex,
|
|
325
|
+
rgbToHsl,
|
|
326
|
+
} from "colorlip";
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
| 関数 | 説明 |
|
|
330
|
+
| --- | --- |
|
|
331
|
+
| `rgbToHex(r, g, b)` | RGB を `#RRGGBB` に変換 |
|
|
332
|
+
| `rgbToHsl(r, g, b)` | RGB を `{ h, s, l }` に変換 |
|
|
333
|
+
| `getHueCategory(hue)` | 色相を `"red" \| "orange" \| ... \| "gray"` に変換 |
|
|
334
|
+
| `createDominantColor(r, g, b, percentage)` | 完全な `ColorlipColor` を生成 |
|
|
335
|
+
| `aggregateColors(colorSets, numColors?)` | 複数の抽出結果を上位色へ集約 |
|
|
336
|
+
| `extractFallbackPalette(...)` | 単純なフォールバック抽出器を直接実行 |
|
|
337
|
+
|
|
338
|
+
## 仕組み
|
|
339
|
+
|
|
340
|
+
抽出パイプラインは次の流れです。
|
|
341
|
+
|
|
342
|
+
1. アダプター経由の入力は最大 `150x150` まで縮小されます。生ピクセルのコア API はこの段階を持ちません。
|
|
343
|
+
2. サンプリングパスで、中央値彩度、彩度の広がり、エッジがどれだけ中央に集中しているかを推定します。
|
|
344
|
+
3. `alpha < 0.5` のピクセルは無視されます。それ以外は alpha ベースの重み付きで扱われます。
|
|
345
|
+
4. ピクセルは適応的な彩度下限と設定された明度範囲でフィルタされます。
|
|
346
|
+
5. 残ったピクセルは量子化ビンに集約され、中央バイアス、エッジ強度、彩度、alpha に基づいて重み付けされます。
|
|
347
|
+
6. 近いビンは Lab 空間で適応的な Delta E 閾値を使って事前マージされます。
|
|
348
|
+
7. クラスタは、前段で作った重みを土台にしつつ、空間分布、中央/外周バイアス、重心位置、広がり、OKLCH における accent 適性を加味してスコアリングされます。
|
|
349
|
+
8. 最終スウォッチは、さらに適応的な Delta E マージを通して選ばれます。
|
|
350
|
+
9. `dominant` は単純な最上位色ではなく、上位スウォッチを OKLCH 上の代表らしさも使って再評価して選ばれます。
|
|
351
|
+
10. `accent` は知覚的に十分離れた候補またはスウォッチから選ばれます。
|
|
352
|
+
11. メイン経路で候補が得られなければ、より単純なヒストグラムベースのフォールバック抽出が使われます。
|
|
353
|
+
|
|
354
|
+
## Dominant の選び方
|
|
355
|
+
|
|
356
|
+
`palette.dominant` は 2 段階で決まります。
|
|
357
|
+
|
|
358
|
+
1. まず候補色を抽出し、重みや空間情報からスコア順に並べます
|
|
359
|
+
2. その後、最終スウォッチの上位色を対象に、元のスコア、重み、OKLCH 上の代表らしさを使って再評価します
|
|
360
|
+
|
|
361
|
+
この再評価を入れることで、面積の広い暗部がそのまま勝つよりも、「その画像らしく見える色」が `dominant` になりやすくなっています。
|
|
362
|
+
|
|
363
|
+
## Alpha の扱い
|
|
364
|
+
|
|
365
|
+
入力に alpha チャンネルがある場合:
|
|
366
|
+
|
|
367
|
+
- `alpha < 128` のピクセルは無視されます
|
|
368
|
+
- `alpha >= 128` のピクセルは採用されます
|
|
369
|
+
- 寄与度は `(alpha / 255) ** 2` で重み付けされます
|
|
370
|
+
|
|
371
|
+
これにより、半透明の縁ノイズを抑えつつ、実際に見えている色は抽出に反映できます。
|
|
372
|
+
|
|
373
|
+
## 補足
|
|
374
|
+
|
|
375
|
+
- コアは `Uint8Array` と `Uint8ClampedArray` の両方を受け付けます
|
|
376
|
+
- `channels` は `3` または `4` を想定しています
|
|
377
|
+
- ブラウザで文字列ソースを渡す場合は `fetch(...)` で取得されます
|
|
378
|
+
- ブラウザアダプターの縮小処理は `OffscreenCanvas` を使います
|
|
379
|
+
- グレースケール画像や強くフィルタされた画像では、単純なフォールバック経路に落ちる場合があります
|
|
380
|
+
|
|
381
|
+
## 互換エイリアス
|
|
382
|
+
|
|
383
|
+
以下の名前は互換性のため残っています。
|
|
384
|
+
|
|
385
|
+
- `DominantColor` は `ColorlipColor` の型エイリアス
|
|
386
|
+
- `colorlip(...)`
|
|
387
|
+
- `colorlipFromFile(...)`
|
|
388
|
+
- `colorlipFromBuffer(...)`
|
|
389
|
+
- `colorlipFromImage(...)`
|
|
390
|
+
- `colorlipFromImageData(...)`
|
|
391
|
+
|
|
392
|
+
## ライセンス
|
|
393
|
+
|
|
394
|
+
[MIT](./LICENSE)
|