Skip to main content
colorscope

Extraction

Extract dominant colors from images using quantization and OKLab merging. Detect and measure border regions in product photos. Node-only — requires sharp.

Material Swatch

Source image

Leather preset interior

Extracted palette

12 colors covering 99% of the visible material.

Background

Corner sampling

Histogram

720 HSL cells

Merge

OKLab d < 0.03

#584d41
31.9%H:30 S:15 L:30
#211a12
28.2%H:30 S:30 L:10
#beb3a7
13.7%H:30 S:15 L:70
#93806c
10.4%H:30 S:15 L:50
#635836
3.2%H:45 S:30 L:30
#864d13
2.9%H:30 S:75 L:30
#e9e7e2
2.2%H:45 S:15 L:90
#a68059
1.8%H:30 S:30 L:50
#734d26
1.6%H:30 S:50 L:30
#331a00
1.4%H:30 S:100 L:10
#bf8040
1.1%H:30 S:50 L:50
#808080
1.0%H:0 S:0 L:50

Pipeline

BackgroundCorner sampling
Histogram720-cell HSL grid
Coverage95% threshold
OKLab Merged < 0.03 merge
const { colors, background } = await extractColorsFromBuffer(buffer); // 12 colors extracted
import { extractColorsFromUrl, extractColorsFromBuffer } from "colorscope/extraction";
import { detectBorderFromBuffer, detectBorderFromUrl } from "colorscope/border";

Color Extraction

extractColorsFromUrl

Extract dominant colors from an image URL.

function extractColorsFromUrl(imageUrl: string): Promise<ExtractionResult>
ParamTypeDescription
imageUrlstringPublic http/https URL (blocks private IPs)

Returns: Up to 12 colors sorted by proportion + background detection metadata.

const { colors, background } = await extractColorsFromUrl("https://example.com/leather-swatch.jpg");
// colors: [{ hue: 30, saturation: 75, lightness: 30, proportion: 0.35, hex: "#8B4513", ... }, ...]
// background: { detected: true, hex: "#e6e6e6" }

extractColorsFromBuffer

Extract dominant colors from a local image buffer.

function extractColorsFromBuffer(buffer: Buffer): Promise<ExtractionResult>
ParamTypeDescription
bufferBufferRaw image buffer (any format supported by sharp)
import { readFile } from "fs/promises";

const buffer = await readFile("leather-swatch.jpg");
const { colors, background } = await extractColorsFromBuffer(buffer);

if (background.detected) {
  console.log(`Studio background filtered: ${background.hex}`);
}

Pipeline

The extraction pipeline processes images through four stages:

  1. Background detection — Samples corners to detect and exclude uniform backgrounds
  2. Histogram counting — Quantizes every pixel to a 720-cell HSL grid (24 hues × 6 saturations × 5 lightnesses)
  3. Proportion coverage — Keeps colors until 95% of foreground pixels are covered
  4. OKLab merging — Merges perceptually similar colors (distance < 0.03) and filters JPEG artifacts

Images are resized to 200×200px before processing for speed.

Border Detection

detectBorderFromBuffer

Detect uniform border regions in an image buffer — useful for cropping product photos with solid-color frames.

async function detectBorderFromBuffer(
  buffer: Buffer,
  options?: DetectBorderOptions
): Promise<BorderBounds>
ParamTypeDescription
bufferBufferImage data (any format supported by sharp)
optionsDetectBorderOptionsDetection parameters
const border = await detectBorderFromBuffer(buffer);
if (border.color) {
  console.log(`Border: ${border.color}, top: ${border.top.width}px`);
  console.log(`Content: ${border.contentBox.width}×${border.contentBox.height}`);
}

detectBorderFromUrl

async function detectBorderFromUrl(
  url: string,
  options?: DetectBorderOptions
): Promise<BorderBounds>

Fetch image from URL and detect borders. SSRF-protected — public http/https only.

How Border Detection Works

  1. Corner sampling — 5×5 pixel patches at each corner, converted to OKLab
  2. Edge scanning — Scans inward, measuring rows/columns matching border color
  3. Confidence scoring — 0-1 based on transition sharpness
  4. Coordinate scaling — Widths reported in original image coordinates

Types

ExtractionResult

interface ExtractionResult {
  colors: QuantizedColor[];
  background: {
    detected: boolean;
    hex: string | null;
  };
}

BorderBounds

interface BorderBounds {
  top: EdgeBound;
  right: EdgeBound;
  bottom: EdgeBound;
  left: EdgeBound;
  color: string | null;
  contentBox: { x: number; y: number; width: number; height: number };
}

EdgeBound

interface EdgeBound {
  width: number;      // Pixels (original coordinates)
  confidence: number; // 0-1 transition sharpness
}

DetectBorderOptions

interface DetectBorderOptions {
  colorThreshold?: number;       // OKLab distance (default: 0.05)
  uniformityThreshold?: number;  // Row/column match fraction (default: 0.95)
  maxResolution?: number;        // Downscale target (default: 400)
}