You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

UNDR-detector is released for non-commercial research use. By requesting access you agree not to use the model or its outputs for commercial purposes.

Log in or Sign Up to review the conditions and access this model content.

UNDR-detector: UPRIO Notebook Detection and Redability Detector

A compact unified detector–classifier for gradability — deciding whether an automatic grader should be invoked on a photograph of handwritten student work at all. A YOLOv8s backbone is shared between a notebook detection head and a 3-class gradability head; a detection-guided pooling operator (DetGuidedPool) uses per-region detector confidence to weight the classifier's spatial pooling, so the gradability decision is read out from the localized notebook region rather than the whole frame.

From Gating Vision–Language Graders: A Multi-Task Detector for Reliable Evaluation of Handwritten Student Work Captured In-the-Wild (BMVC 2026). Evaluation data: uprio/UNDR.

Usage

Access is gated, so authenticate once. --add-to-git-credential is what lets the pip install below reach the repo:

hf auth login --add-to-git-credential
pip install git+https://huggingface.co/uprio/UNDR-detector
import json, cv2, torch
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
from undr.model import MultiTaskModel
from undr.letterbox import letterbox, letterbox_params

REPO = "uprio/UNDR-detector"
cfg = json.load(open(hf_hub_download(REPO, "config.json")))
model = MultiTaskModel(cfg)
model.load_state_dict(load_file(hf_hub_download(REPO, "model.safetensors")), strict=False)
model.eval()

image_rgb = cv2.cvtColor(cv2.imread("page.jpg"), cv2.COLOR_BGR2RGB)
canvas = letterbox(image_rgb, target_size=480)
x = torch.from_numpy(canvas).permute(2, 0, 1).float().div(255).unsqueeze(0)

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

probs = torch.softmax(out.logits, dim=-1)[0]

Decision rules

These are calibrated, not defaults — plain argmax gives different results:

BAD_THRESHOLD      = 0.35   # predict "bad" when P(bad) >= this,
                            # else argmax over (good, not-a-notebook)
DET_CONF_THRESHOLD = 0.25   # objectness below this -> no crop
MIN_BOX_AREA       = 0.001  # normalized area; rejects degenerate boxes
if probs[0] >= 0.35:
    label = "bad"
else:
    label = ["bad", "good", "not-a-notebook"][int(probs[1:].argmax()) + 1]

The 3-class labels are 0 = bad, 1 = good, 2 = not-a-notebook. The deployed two-class contract collapses not-a-notebook into bad.

Bounding box

out.detect_out is (pred_bbox, pred_obj). The box is normalized cxcywh in letterbox canvas coordinates — the letterbox transform must be inverted to land on original pixels:

bbox, obj = out.detect_out
conf = torch.sigmoid(obj[0]).item()
cx, cy, bw, bh = bbox[0].tolist()

native_h, native_w = image_rgb.shape[:2]
new_h, new_w, top, _bot, left, _right = letterbox_params(native_h, native_w, 480)
scale_x, scale_y = new_w / native_w, new_h / native_h

x1 = ((cx - bw / 2) * 480 - left) / scale_x
x2 = ((cx + bw / 2) * 480 - left) / scale_x
y1 = ((cy - bh / 2) * 480 - top) / scale_y
y2 = ((cy + bh / 2) * 480 - top) / scale_y

x1, x2 = [int(max(0, min(round(v), native_w))) for v in (x1, x2)]
y1, y2 = [int(max(0, min(round(v), native_h))) for v in (y1, y2)]

use_box = conf >= 0.25 and (bw * bh) >= 0.001
crop = image_rgb[y1:y2, x1:x2] if use_box else image_rgb

When the classifier predicts not-a-notebook, objectness is forced to ~0 so the detection is suppressed.

Preprocessing

RGB (not BGR), letterbox to 480×480 with BORDER_REFLECT_101 padding, /255, NCHW. Images smaller than 480 are center-padded rather than upscaled, preserving native handwriting frequencies. Use the bundled undr/letterbox.py rather than reimplementing — the border mode matters.

Results

Measured on the 6,609 images of uprio/UNDR (pre-anonymization originals), batch size 32, with the decision rules above and orientation applied before letterboxing:

Metric Value
Quality 3-class accuracy 92.60
Quality macro F₁ 93.20
bad F₁ 88.58
good F₁ 94.14
not-a-notebook F₁ 96.89
Detection recall@0.5 98.87
Detection precision@0.5 99.29
Detection mean IoU (fired) 91.24

Citation

@inproceedings{sridhar2026gating,
  title     = {Gating Vision--Language Graders: A Multi-Task Detector for Reliable
               Evaluation of Handwritten Student Work Captured In-the-Wild},
  author    = {Sridhar, Sashank and Adavanne, Sharath},
  booktitle = {British Machine Vision Conference (BMVC)},
  year      = {2026}
}
Downloads last month
-
Safetensors
Model size
14.8M params
Tensor type
F32
·
BOOL
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support