md2link

Color by Number — Pipeline Algorithm Specification

DraftMay 26, 2026

Color by Number — Pipeline Algorithm Specification

Pipeline tổng quan

PNG/JPEG input


[1] vectorizer.ai API (fill_shapes, max_colors=20)


  SVG vector

     ├──[2]──→ after/  (full color PNG)
     ├──[3]──→ before/ (line art PNG)
     └──[4]──→ zip/    (Iceors ZIP)

[1] Vectorize: PNG → SVG

POST https://api.vectorizer.ai/api/v1/vectorize
Auth: HTTP Basic (API_ID, API_SECRET)
Body: multipart/form-data
  image                      = <file bytes>
  output.file_format         = "svg"
  mode                       = "production"
  processing.max_colors      = "20"
  output.gap_filler.enabled  = "true"

Response: raw SVG bytes (200 OK)
Cost: 1 credit/ảnh

[2] After PNG (full color)

Input:  SVG bytes từ bước 1
Tool:   cairosvg.svg2png(bytestring=svg, output_width=2048, output_height=2048)
Output: PNG 2048x2048, full color
Save:   after/{name}.png

[3] Before PNG (line art)

Input:  SVG bytes từ bước 1
Logic:
  1. Parse SVG bằng svgelements
  2. Duyệt mọi Shape element
  3. Chỉ giữ element có:
     - stroke != none (có nét vẽ)
     - stroke_width > 0
     - stroke luminance < 200
       (luminance = 0.299*R + 0.587*G + 0.114*B)
  4. Render mỗi path giữ lại thành:
     <path d="..." fill="none" stroke="black"
           stroke-width="{gốc}" stroke-linecap="round"
           stroke-linejoin="round"/>
  5. Đặt trên nền trắng:
     <rect fill="white" phủ toàn bộ viewBox/>
  6. cairosvg.svg2png → PNG 2048x2048

Output: PNG 2048x2048, đen trắng (lineart)
Save:   before/{name}.png

[4] Iceors ZIP

ZIP chứa 3 files, không có extension:

{key}_b.zip
├── {key}b              ← path data (text, CRLF line ending)
├── {key}c              ← reference JPEG 2048x2048
└── sp_new_paint_flag   ← nội dung: "111\r\n" (5 bytes)

{key} = tên file gốc bỏ ký tự đặc biệt, max 20 chars. VD: anh1.png → key = anh1


File {key}b — Path data

Mỗi dòng là 1 region, ngăn cách bằng \r\n (CRLF). 6 fields phân cách bằng |:

{path_d}|{color}|{stroke_width}|{label_pos}|{font_size}|{flag}

3 loại dòng:

Loại color stroke_width label_pos font_size flag Ý nghĩa
Fill màu 8A572D (hex RGB) 0 cy*2048+cx 10-124 0 Vùng user tô được
Fill đen 000000 0 0 0 0 Vùng cố định (decoration)
Stroke 0 2 (int px) 0 0 0 Nét viền

Công thức từng field

path_d

SVG path commands (M, L, C, Q, Z — uppercase absolute coords). Tọa độ trong canvas 2048x2048.

VD: M957.87,299.21C30.52,1.46...Z

color

  • Fill: hex 6 chars (VD: 8A572D), 000000 nếu đen
  • Stroke: 0
  • Lưu ý: white (255,255,255) → FEFEFE

stroke_width

  • Fill: 0
  • Stroke: int pixel width (VD: 2)

label_pos

  • Fill: cy * 2048 + cx (cx, cy = tâm bounding box của path)
  • Stroke: 0

font_size

  • Fill: tính theo diện tích vùng:
ratio = area / (2048 * 2048)

if ratio > 0.05:
    font_size = clamp(ratio * 800, 10, 124)
elif ratio > 0.01:
    font_size = clamp(ratio * 1500, 10, 30)
else:
    font_size = max(10, ratio * 3000)
  • Stroke: 0

flag

  • Fill: 0
  • Stroke: 0

Thứ tự dòng

  1. Fill lines trước (sort diện tích giảm dần — vùng lớn nhất trước)
  2. Stroke lines sau

Xử lý đặc biệt trước khi xuất

  • Merge similar colors: các shade RGB cách nhau ≤ 15 (Euclidean) → gộp về shade lớn nhất
  • Subtract overlaps: mỗi fill chỉ giữ pixel KHÔNG bị fill nào ở trên che (dùng boolean path DIFFERENCE)
  • Auto outline: mỗi fill tự động sinh thêm 1 stroke line đen width=1.5px cùng path

File {key}c — Reference JPEG

Input:  SVG bytes
Logic:
  1. cairosvg.svg2png(svg, output_width=W, output_height=H)
     W, H = scale SVG về max cạnh = 2048, giữ aspect ratio
  2. PIL resize → (W, H)
  3. Center-pad trắng → 2048x2048
  4. Save JPEG quality=92

Output: JPEG 2048x2048

File sp_new_paint_flag

Nội dung cố định: "111\r\n" (5 bytes, ASCII)

Hệ tọa độ

Canvas: 2048 x 2048 pixels (vuông)
Gốc tọa độ: top-left (0, 0)
SVG không vuông: pad center vào khung vuông trước khi scale

label_pos encoding:
  pack:   label_pos = y * 2048 + x
  unpack: x = label_pos % 2048
          y = label_pos // 2048

Cấu trúc Output

output/
├── before/                     ← Line art PNG (black strokes on white)
│   ├── anh1.png                   2048x2048
│   ├── anh2.png
│   └── ...
├── after/                      ← Full color PNG (rendered from SVG)
│   ├── anh1.png                   2048x2048
│   ├── anh2.png
│   └── ...
└── zip/                        ← Iceors ZIP (load vào Android app)
    ├── anh1_b.zip
    │   ├── anh1b                  path data (pipe-delimited, CRLF)
    │   ├── anh1c                  reference JPEG 2048x2048
    │   └── sp_new_paint_flag      "111\r\n"
    ├── anh2_b.zip
    └── ...

CLI Usage

cd tools/

# Batch processing
python batch_pipeline.py input_folder/ output_folder/ \
  --api-id <VECTORIZER_API_ID> \
  --api-secret <VECTORIZER_API_SECRET> \
  --max-colors 20 \
  --workers 1

# Hoặc dùng environment variables
export VECTORIZER_API_ID="your_id"
export VECTORIZER_API_SECRET="your_secret"
python batch_pipeline.py input/ output/

CLI Options

Flag Default Mô tả
--max-colors 20 Số màu tối đa cho vectorizer
--dark-threshold 200 Ngưỡng luminance cho line art (0-255)
--workers 1 Số thread chạy song song
--api-id env var Vectorizer.ai API Id
--api-secret env var Vectorizer.ai API Secret