File size: 5,224 Bytes
2c7a090
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
---
license: apache-2.0
language:
- ko
tags:
- hangul
- korean
- font-classification
- font-recognition
- image-classification
- pytorch
pipeline_tag: image-classification
---

# Hangul Font Classification Model (HFC)

A PyTorch model that recognizes **which font** a printed Hangul (Korean) syllable glyph
was rendered in, and jointly recognizes **which syllable** the glyph represents.

Given a single 64x64 grayscale glyph image, the model predicts:

- **Font** β€” the source font, out of 3,480 classes (`model/index.csv`)
- **Character** β€” the Hangul syllable, decoded from separately predicted
  cho (initial consonant) / jung (medial vowel) / jong (final consonant) logits

## Model Details

- **Architecture**: shared ResNet-style CNN encoder β†’ global average pooling β†’
  two projection heads that split the pooled feature into a *content* code
  (128-dim, describes which syllable it is) and a *style* code (512-dim, L2
  normalized, describes which font it is)
  - **Hangul head**: three small MLPs on the content code, predicting
    cho / jung / jong logits independently (19 / 21 / 28 classes)
  - **Font head**: an MLP + linear classifier on the style code, predicting
    font logits (3,480 classes)
  - **Decoder**: reconstructs the 64x64 input from `concat(content, style)`;
    used only as an auxiliary training signal, not required for inference
- **Input**: single-channel (grayscale) 64x64 image, black glyph on white
  background, pixel values scaled to `[0, 1]` (no further normalization)
- **Output**: `cho_logits`, `jung_logits`, `jong_logits`, `font_logits`
  (plus the intermediate `content` / `style` codes and, if `forward()` is
  used, a reconstructed image)
- **Framework**: PyTorch (`torch>=2.6`)
- **File**: [`model/hfc.pt`](model/hfc.pt) β€” a checkpoint dict with a
  `"model"` key holding the `state_dict`
- **Font label map**: [`model/index.csv`](model/index.csv) β€” maps each
  `id` (0-3479) used by `font_logits` to its human-readable `font_name`

## Intended Use

This model is intended for **font identification / classification** of
printed Hangul glyphs (e.g. document analysis, digital typography tooling,
font cataloguing) and for research on Hangul character recognition. It is
**not** intended to generate, synthesize, or reproduce font glyphs β€” the
decoder exists only to regularize training and is not distributed as a
generative tool.

## Usage

Model definition and a runnable example live under [`example/`](example):

- [`example/model.py`](example/model.py) β€” `FontRecognitionModel` and the
  Hangul cho/jung/jong composition/decomposition utilities
- [`example/demo.py`](example/demo.py) β€” loads the checkpoint, runs
  inference on a sample glyph, and prints the predicted character and
  top-k font candidates

```bash
uv sync
uv run python example/demo.py --image path/to/glyph.png --topk 10
```

Minimal inference snippet:

```python
import torch
from PIL import Image

from example.model import CHAR_SIZE, FontRecognitionModel, decode_open, decode_restricted

checkpoint = torch.load("model/hfc.pt", map_location="cpu", weights_only=False)

num_font_classes = 3480  # number of rows in model/index.csv
model = FontRecognitionModel(num_font_classes)
model.load_state_dict(checkpoint["model"])
model.eval()

image = Image.open("path/to/glyph.png").convert("L").resize((CHAR_SIZE, CHAR_SIZE))
x = torch.frombuffer(bytearray(image.tobytes()), dtype=torch.uint8)
x = x.reshape(1, 1, CHAR_SIZE, CHAR_SIZE).float() / 255.0

with torch.no_grad():
    out = model.encode(x)

print(decode_restricted(out.cho_logits, out.jung_logits, out.jong_logits))  # KS X 1001, 2,350 chars
print(decode_open(out.cho_logits, out.jung_logits, out.jong_logits))        # all 11,172 syllables
print(out.font_logits.softmax(-1).topk(5))
```

The image must be a single Hangul glyph, roughly centered, black-on-white,
and will be resized to 64x64 if it isn't already.

## Training Data

This model was trained on a private dataset of scanned images of printed Hangul characters. The dataset contains Hangul syllable images generated from multiple fonts and collected through a print-and-scan process.

The training dataset is not redistributed with this model.

Users should note that the source fonts may be governed by their respective licenses. This model is intended for font classification and research use, and it is not designed to reproduce or generate font glyphs.

## Limitations

- Font coverage is limited to the 3,480 fonts listed in `model/index.csv`;
  fonts not seen during training cannot be predicted correctly.
- The model expects a single, roughly-centered glyph per image, not full
  lines or pages of text β€” it does not perform text detection or
  segmentation.
- Because training images come from a print-and-scan pipeline, performance
  on purely digital (anti-aliased, vector-rendered) glyphs may differ from
  the reported training-time behavior.

## License

The model weights and code in this repository are released under the
[Apache License 2.0](LICENSE). This license covers the model and code
only β€” it does not extend any rights to the third-party fonts referenced
in `model/index.csv`, which remain governed by their own respective
licenses.