npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wangdandan810012/bead-core

v0.3.0

Published

蛋蛋拼豆核心库:图片转拼豆图纸,CIEDE2000 感知配色

Readme

@wangdandan810012/bead-core

蛋蛋拼豆核心算法库 — 将 RGBA 像素图转换为拼豆色号网格,支持 CIEDE2000 感知配色、区域合并、背景识别与编辑工具。

安装

npm install @wangdandan810012/bead-core

本包为 ESM 模块,请在 package.json 中设置 "type": "module",或在 TypeScript 中使用 "module": "ESNext"

核心概念

色板 PaletteEntry[]

每个色号包含唯一 ID、显示色值,以及各品牌对应的豆号:

import type { PaletteEntry } from '@wangdandan810012/bead-core'

const palette: PaletteEntry[] = [
  {
    id: 'red-01',
    hex: '#E74C3C',
    codes: { MARD: 'A1', COCO: 'A1', MANMAN: 'A1', PANPAN: 'A1', MIXIAOWO: 'A1' },
  },
  {
    id: 'neutral-001',
    hex: '#FFFFFF',
    codes: { MARD: 'H1', COCO: 'H1', MANMAN: 'H1', PANPAN: 'H1', MIXIAOWO: 'H1' },
  },
]

codes 的键为品牌体系 BrandSystemMARD | COCO | MANMAN | PANPAN | MIXIAOWO。不使用某品牌时填空字符串即可。

像素数据

所有图像输入均为 RGBA 格式Uint8ClampedArray,长度 = width × height × 4,按行优先排列。

输出网格 MappedGrid

二维数组 MappedCell[][],每个格子包含:

| 字段 | 说明 | |------|------| | paletteId | 匹配到的色板 ID | | hex | 显示用十六进制色值 | | isExternal | 可选,标记为外部背景(不计入拼豆数量) |


快速开始

完整流程:读取图片 → 预处理 → 生成图纸 → 统计色号

import {
  prepareSourcePixels,
  runPipeline,
  computeColorStats,
  countTotalBeads,
  trimGrid,
  type PaletteEntry,
  type BrandSystem,
} from '@wangdandan810012/bead-core'

// 1. 准备色板与像素(见下方「浏览器 / Node 读取图片」)
const palette: PaletteEntry[] = [/* ... */]
const pixels: Uint8ClampedArray = /* RGBA */
const width = 800
const height = 600

// 2. 图像预处理(可选)
const adjusted = prepareSourcePixels(
  pixels, width, height,
  { brightness: 0, contrast: 12, saturation: 0 },  // 亮度 / 对比度 / 饱和度
  { denoise: false, sharpen: true },                  // 降噪 / 锐化
)

// 3. 运行主流水线
const { grid, width: gridW, height: gridH } = runPipeline(adjusted, width, height, {
  gridWidth: 80,              // 输出网格宽度(高度按比例自动计算)
  mode: 'average',            // 'average' 照片 | 'dominant' 卡通/像素风
  mergeThreshold: 5,          // 相似色合并阈值,0 = 不合并
  maxColors: 0,               // 最大颜色数,0 = 不限
  palette,
  backgroundPaletteIds: ['neutral-001'],  // 视为背景的色号 ID
  excludedPaletteIds: [],               // 排除的色号(会重映射到最近色)
})

// 4. 裁掉四周空白背景(可选)
const trimmed = trimGrid(grid)

// 5. 统计色号
const brand: BrandSystem = 'MARD'
const stats = computeColorStats(trimmed, brand, (paletteId, b) => {
  const entry = palette.find((p) => p.id === paletteId)
  return entry?.codes[b] ?? paletteId
})

console.log(`网格 ${gridW}×${gridH},共 ${countTotalBeads(trimmed)} 颗豆`)
console.log(stats) // [{ paletteId, hex, count, displayCode }, ...]

浏览器:从 Canvas 读取像素

import { runPipeline, type PaletteEntry } from '@wangdandan810012/bead-core'

async function imageToGrid(imageUrl: string, palette: PaletteEntry[]) {
  const img = new Image()
  img.crossOrigin = 'anonymous'
  img.src = imageUrl
  await img.decode()

  const canvas = document.createElement('canvas')
  canvas.width = img.naturalWidth
  canvas.height = img.naturalHeight
  const ctx = canvas.getContext('2d')!
  ctx.drawImage(img, 0, 0)

  const { data, width, height } = ctx.getImageData(0, 0, canvas.width, canvas.height)

  return runPipeline(data, width, height, {
    gridWidth: 64,
    mode: 'average',
    mergeThreshold: 0,
    maxColors: 0,
    palette,
    backgroundPaletteIds: [],
    excludedPaletteIds: [],
  })
}

Node.js:从文件读取像素

Node 本身不提供图像解码,需配合 sharp 等库:

import sharp from 'sharp'
import { runPipeline, type PaletteEntry } from '@wangdandan810012/bead-core'

async function fileToGrid(filePath: string, palette: PaletteEntry[]) {
  const { data, info } = await sharp(filePath)
    .ensureAlpha()
    .raw()
    .toBuffer({ resolveWithObject: true })

  return runPipeline(new Uint8ClampedArray(data), info.width, info.height, {
    gridWidth: 64,
    mode: 'average',
    mergeThreshold: 0,
    maxColors: 0,
    palette,
    backgroundPaletteIds: [],
    excludedPaletteIds: [],
  })
}

sharp 不是本库依赖,需自行安装:npm install sharp


流水线参数说明

runPipeline 按以下顺序执行各步骤:

convertImageToPattern → mergeSimilarRegions → limitGridColors
  → markExternalBackground → remapExcludedColors

| 参数 | 类型 | 说明 | |------|------|------| | gridWidth | number | 输出网格列数,行数 = round(gridWidth × 原图高 / 原图宽) | | mode | 'average' \| 'dominant' | 照片用 average(区域均值配色),卡通/像素风用 dominant(主色) | | mergeThreshold | number | CIEDE2000 色差阈值,合并相邻相似色块;0 关闭 | | maxColors | number | 限制最终颜色种类数;0 不限 | | palette | PaletteEntry[] | 可用色板,不能为空 | | backgroundPaletteIds | string[] | 从网格四边洪泛填充,标记为 isExternal: true | | excludedPaletteIds | string[] | 排除的色号,自动重映射到色板中最近色 |


分步调用(高级)

不需要完整流水线时,可单独使用各模块:

import {
  convertImageToPattern,
  mergeSimilarRegions,
  markExternalBackground,
  limitGridColors,
  remapExcludedColors,
} from '@wangdandan810012/bead-core'

// 仅做图片 → 网格转换
let grid = convertImageToPattern(pixels, width, height, {
  gridWidth: 64,
  mode: 'dominant',
  palette,
  excludedPaletteIds: [],
  despeckle: false,  // 去除孤立杂点(仅 average 模式有效)
})

grid = mergeSimilarRegions(grid, 5)
grid = limitGridColors(grid, palette, 20)
grid = markExternalBackground(grid, ['neutral-001'])
grid = remapExcludedColors(grid, palette, ['old-color-id'])

编辑工具

所有编辑函数返回新网格(不可变),不修改原数组。

import {
  fillRegion,
  paintRect,
  trimGrid,
  flipGridHorizontal,
  flipGridVertical,
  cloneGrid,
} from '@wangdandan810012/bead-core'

// 油漆桶:填充同色连通区域
grid = fillRegion(grid, row, col, 'red-01', '#E74C3C')

// 矩形上色
grid = paintRect(grid, row0, col0, row1, col1, 'blue-01', '#3498DB')

// 裁掉四周全为 external 的行列
grid = trimGrid(grid)

// 翻转
grid = flipGridHorizontal(grid)
grid = flipGridVertical(grid)

// 深拷贝
const copy = cloneGrid(grid)

统计与分区

import {
  computeColorStats,
  countTotalBeads,
  countCompleted,
  getConnectedRegions,
} from '@wangdandan810012/bead-core'

// 各色号用量(按 count 降序)
const stats = computeColorStats(grid, 'MARD', codeLookup)

// 非 external 格子总数
const total = countTotalBeads(grid)

// 拼豆进度(completedCells 为 "row,col" 字符串集合)
const done = countCompleted(grid, new Set(['0,0', '1,2']))

// 某色号的所有连通区域(按面积降序)
const regions = getConnectedRegions(grid, 'red-01')
// [{ cells: [{row, col}, ...], paletteId: 'red-01' }, ...]

API 一览

| 分类 | 导出 | 说明 | |------|------|------| | 流水线 | runPipeline | 完整生成流程 | | 转换 | convertImageToPattern | 图片 → 网格 | | 转换 | mapImageToGrid | convertImageToPattern 别名 | | 预处理 | prepareSourcePixels | 亮度 / 对比度 / 饱和度 / 锐化 / 降噪 | | 预处理 | applyImageAdjustments / applyPhotoOptimize | 分步预处理 | | 合并 | mergeSimilarRegions | 相似色区域合并 | | 背景 | markExternalBackground | 外部背景洪泛标记 | | 限色 | limitGridColors | 限制最大颜色数 | | 重映射 | remapExcludedColors | 排除色重映射 | | 编辑 | fillRegion / paintRect | 填充 / 矩形上色 | | 编辑 | trimGrid / flipGridHorizontal / flipGridVertical | 裁边 / 翻转 | | 工具 | cloneGrid / gridDimensions | 克隆 / 尺寸 | | 统计 | computeColorStats / countTotalBeads | 色号统计 / 总数 | | 统计 | countCompleted / getConnectedRegions | 进度 / 连通域 | | 色彩 | findClosestPaletteEntry / deltaE2000 | 最近色 / 色差 | | 色彩 | hexToRgb / rgbToHex | 颜色转换 | | 类型 | PaletteEntry, MappedGrid, PipelineOptions 等 | TypeScript 类型 |

完整算法说明见 docs/algorithms


开发

git clone https://github.com/whr810012/bead-core.git
cd bead-core
npm install
npm test        # 运行测试
npm run build   # 编译到 dist/

License

MIT — Copyright (c) 2026 蛋蛋 — 见 LICENSE

致谢

算法思路受以下开源项目启发(本库为独立 TypeScript 实现):