# 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

```bash
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 |
