--- license: mit library_name: onnx pipeline_tag: image-feature-extraction language: - mul inference: false tags: - onnx - computer-vision - font-recognition - typography - image-embeddings - document-analysis - multi-task --- # 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: ```python 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: ```text 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 ```python 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: ```python 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.