FontDNA

FontDNA is an ONNX model for recognizing typography from word-image crops.

It returns a font embedding plus word-local typography predictions. Font family is not a fixed classifier: compare the embedding against embeddings of your own reference fonts.

Model I/O

Inputs

  • img โ€” float32, shape (B, 1, 40, W)
  • cols โ€” int64, shape (B,); real crop width divided by 8

Word crops are grayscale, resized to height 40 while preserving aspect ratio. Width is aligned to multiples of 8 (max 320). Scale pixels to [0,1], then normalize each crop with:

x = (x - x.mean()) / (x.std() + 1e-4)

Outputs, in order

# Output Decode as
0 font embedding L2-normalize, then use cosine similarity
1 style logits [4] sigmoid โ†’ bold, italic, underline, strikethrough
2 category logits [5] softmax โ†’ serif, sans-serif, display, handwriting, monospace
3 confidence logit sigmoid
4 script logits [14] softmax โ†’ mapping below

Script indices:

0 latin          5 hangul          10 tamil
1 cyrillic       6 arabic          11 thai
2 greek          7 hebrew          12 armenian
3 han            8 devanagari      13 georgian
4 kana           9 bengali

Styles are per word. For example, one underlined word can still share the same font-family embedding/section as the surrounding regular words.

Raw ONNX Runtime example

import numpy as np
import onnxruntime as ort

session = ort.InferenceSession("glyphdna.onnx")

# img: preprocessed float32 array shaped (B, 1, 40, W)
# cols: int64 array shaped (B,), where cols[i] = real_width // 8
embedding, style_logits, category_logits, confidence_logits, script_logits = (
    session.run(None, {"img": img, "cols": cols})
)

sigmoid = lambda x: 1 / (1 + np.exp(-np.clip(x, -60, 60)))
softmax = lambda x: np.exp(x - x.max(-1, keepdims=True)) / np.exp(
    x - x.max(-1, keepdims=True)
).sum(-1, keepdims=True)

embedding = embedding / np.maximum(
    np.linalg.norm(embedding, axis=1, keepdims=True), 1e-8
)
styles = sigmoid(style_logits)
categories = softmax(category_logits)
confidence = sigmoid(confidence_logits).reshape(-1)
scripts = softmax(script_logits)

# Example: first word
print("bold:", styles[0, 0])
print("italic:", styles[0, 1])
print("underline:", styles[0, 2])
print("strikethrough:", styles[0, 3])
print("category index:", categories[0].argmax())
print("script index:", scripts[0].argmax())

If your exported ONNX uses different tensor names, inspect them with:

print([x.name for x in session.get_inputs()])
print([x.name for x in session.get_outputs()])

Font matching

To recognize a font family, embed reference word renders from each candidate font with the same model and preprocessing. L2-normalize the vectors and rank candidates by cosine similarity. Combining several neighboring word embeddings is usually more reliable than trusting a single word, because some words contain few font-discriminative glyphs.

A standalone Python library for indexing fonts, segmenting images, clustering same-font sections, and preserving per-word formatting is planned separately and is expected to be released in a few weeks.

Notes

  • This model is not OCR and does not transcribe text.
  • Exact font names come from your own reference/index collection.
  • Bold, italic, underline, strikethrough, category, and script are predicted per word.
  • Precompiled font indexes must use the same model weights and preprocessing.

License

MIT.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support