Views
No views yet
┌─────────────────────────────────────────────────────────────┐
│ INPUT: Damaged Calligraphy │
│ (RGB, H×W×3) │
└────────────────────────┬────────────────────────────────────┘
│
┌───────────────▼───────────────┐
│ STAGE 1: PATHFINDING │
│ (Deterministic Algorithm) │
│ │
│ 1. Binarize → ink mask │
│ 2. Skeletonize (Zhang-Suen) │
│ 3. Find gap endpoints │
│ 4. A*/Bezier gap bridging │
│ 5. Variable-width rendering │
└───────┬───────────┬───────────┘
│ │
stroke_mask gap_mask
(1,H,W) (1,H,W)
│ │
┌───────▼───────────▼───────────┐
│ STAGE 2: cGAN REFINEMENT │
│ (Learned Style Transfer) │
│ │
│ Generator Input (5ch): │
│ [damaged_RGB(3) + │
│ stroke_mask(1) + │
│ gap_mask(1)] │
│ │
│ ┌─────────────────────────┐ │
│ │ U-Net Generator │ │
│ │ + Gated Convolutions │ │
│ │ + 8 Dilated ResBlocks │ │
│ │ + Skip Connections │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 70×70 SN-PatchGAN │ │
│ │ Discriminator │ │
│ │ (Spectral Normalized) │ │
│ └─────────────────────────┘ │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ OUTPUT: Repaired Image │
│ (RGB, H×W×3) │
└───────────────────────────────┘pip install -r requirements.txt1# Generate 5000 synthetic training pairs
2python damage_generator.py \
3 --output_dir data \
4 --num_train 5000 \
5 --num_val 500 \
6 --image_size 256
7
8# Or use your own clean calligraphy images:
9python damage_generator.py \
10 --output_dir data \
11 --source_dir /path/to/clean/calligraphy/ \
12 --num_train 5000data/
├── train/
│ ├── clean/ # Ground truth images
│ ├── damaged/ # Synthetically damaged images
│ └── mask/ # Damage location masks
└── val/
├── clean/
├── damaged/
└── mask/1# Full training (recommended)
2python train.py \
3 --data_dir data \
4 --epochs 200 \
5 --batch_size 8 \
6 --image_size 256 \
7 --generator_type unet \
8 --gan_type lsgan \
9 --lr_g 1e-4 \
10 --lr_d 1e-4
11
12# Quick test run
13python train.py \
14 --data_dir data \
15 --epochs 5 \
16 --batch_size 4 \
17 --generate_data \
18 --num_train 100 \
19 --num_val 20
20
21# With on-the-fly damage generation (only needs clean/ directory)
22python train.py \
23 --data_dir data \
24 --on_the_fly_damage \
25 --epochs 200
26
27# Resume from checkpoint
28python train.py \
29 --data_dir data \
30 --resume checkpoints/best_model.pth \
31 --epochs 3001# With trained GAN (best quality)
2python inference.py \
3 --input damaged_calligraphy.png \
4 --output repaired.png \
5 --checkpoint checkpoints/generator_best.pth
6
7# Pathfinding only (no GAN needed, instant)
8python inference.py \
9 --input damaged_calligraphy.png \
10 --output repaired.png \
11 --pathfinding_only
12
13# Batch repair a directory
14python inference.py \
15 --input_dir damaged_images/ \
16 --output_dir repaired_images/ \
17 --checkpoint checkpoints/generator_best.pth
18
19# Save all intermediate stages for visualization
20python inference.py \
21 --input damaged_calligraphy.png \
22 --output repaired.png \
23 --checkpoint checkpoints/generator_best.pth \
24 --save_stages| Parameter | Value | Source |
|---|---|---|
| Optimizer | Adam(β1=0.0, β2=0.9) | EdgeConnect |
| Learning Rate | 1e-4 (G and D) | EdgeConnect |
| LR Schedule | Constant first half, linear decay second half | pix2pix |
| GAN Type | LSGAN (MSE loss) | pix2pixHD |
| λ_adversarial | 1.0 | EdgeConnect |
| λ_L1 | 100.0 | pix2pix |
| λ_feature_matching | 10.0 | pix2pixHD |
| λ_perceptual | 0.1 | EdgeConnect |
| λ_style | 250.0 | EdgeConnect |
| λ_masked_L1 | 50.0 | DiffHDR |
| Image Size | 256×256 | Standard |
| Batch Size | 8 | EdgeConnect |
| Epochs | 200 | pix2pix |
| Weight Init | Gaussian(0, 0.02) | pix2pix |
L_G = λ_adv · L_adversarial (fool the discriminator)
+ λ_L1 · L_L1 (pixel-level reconstruction)
+ λ_FM · L_feature_matching (stabilize training via D features)
+ λ_perc · L_perceptual (VGG relu1_2, relu2_2, relu3_3, relu4_3)
+ λ_style · L_style (Gram matrices for texture matching)
+ λ_mask · L_masked_L1 (focus on damaged regions)L_D = L_adversarial_D + λ_R1 · L_R1_regularizationEncoder:
[GatedConv 5→64, 7×7, stride 1] # Level 0
[GatedConv 64→128, 4×4, stride 2] # Level 1 (↓2×)
[GatedConv 128→256, 4×4, stride 2] # Level 2 (↓2×)
Bottleneck:
8× [DilatedResBlock 256→256, dilation=2] # ~200px receptive field
Decoder:
[ConvTranspose 512→128, 4×4, stride 2] # Skip from Level 2 (↑2×)
[ConvTranspose 256→64, 4×4, stride 2] # Skip from Level 1 (↑2×)
[Conv 128→3, 7×7, Tanh] # Skip from Level 0
All layers: InstanceNorm + ReLU (encoder: LeakyReLU)
Input: 5 channels (damaged_RGB + stroke_mask + gap_mask)
Output: 3 channels (repaired RGB in [-1, 1])C64(no norm) → C128 → C256 → C512 → Conv→1
All convs: 4×4, spectral normalized
InstanceNorm on layers 2-4
LeakyReLU(0.2) throughout
Input: 8 channels (damaged + output + masks)
Output: H/16 × W/16 patch predictions├── pathfinding.py # Stage 1: Deterministic gap repair
├── damage_generator.py # Synthetic training data generation
├── models.py # cGAN architecture (Generator + Discriminator)
├── losses.py # Loss functions + metrics
├── dataset.py # Dataset loader with pathfinding integration
├── train.py # Training pipeline
├── inference.py # Inference / repair script
├── requirements.txt # Dependencies
└── README.md # This file1# Generate paired data from your clean images
2python damage_generator.py \
3 --source_dir /path/to/your/clean/calligraphy/ \
4 --output_dir data \
5 --num_train 5000
6
7# Or train with on-the-fly damage
8python train.py \
9 --data_dir data \
10 --on_the_fly_damagedata/
├── train/
│ ├── clean/001.png, 002.png, ...
│ └── damaged/001.png, 002.png, ...
└── val/
├── clean/001.png, 002.png, ...
└── damaged/001.png, 002.png, ...checkpoints/samples/ for visual progress--max_gap_distance controls how large a gap the pathfinder will attempt to bridge