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
- -