garment-crop-gate-nano

A 47,122-parameter pre-flight gate for resoa/garment-attributes. 188 KB.

Answers one question before the 93.1M model runs: is this crop tight enough to trust the output?

Why this exists

garment-attributes states that it needs single-garment crops and that full-scene inputs "degrade accuracy sharply". That warning is correct and qualitative β€” it tells you a gate is needed without telling you where to put it. Fed a whole photograph, the model returns confident sigmoid scores across all 218 attributes with no signal that anything is wrong.

So the degradation was measured. Fashionpedia val2020, 3,711 instances, 212 evaluable labels β€” the model's own published protocol. Only the crop padding varies; preprocessing, threshold and metric are identical throughout.

padding micro-F1 macro-mAP mAP retained
0.08 (as trained) 0.6995 0.4229 100%
0.25 0.7006 0.4355 103%
0.50 0.6522 0.3842 91%
1.00 0.5441 0.2403 57%
2.00 0.4307 0.1260 30%
full scene 0.3550 0.0583 14%

"Sharply" is βˆ’86%. A full scene keeps 14% of trained-crop mAP.

A second, free finding: 0.25 padding beats the recommended 0.08 by +3% mAP (0.4355 vs 0.4229). Slightly more context helps. That is an improvement to the existing model at no cost and no retraining β€” just widen the crop.

Reproduction, before any of the above is believable

Published: micro-F1 0.7100, macro-mAP 0.4417, 3,711 instances, 212 labels. This harness: micro-F1 0.6995, macro-mAP 0.4229, 3,711 instances, 212 labels.

Instance and label counts match exactly once the card's 48-pixel minimum-crop filter is applied. The residual ~1–2% gap is unexplained and most likely crop-rounding or resize detail. The curve above is relative to this harness's own 0.08 baseline, so that offset cancels; the absolute numbers should be read as ~2% low.

Does the gate actually help? Measured end-to-end

The gate is only worth its inference cost if it improves the system. Simulating a user feeding variably-cropped inputs (each held-out instance assigned a random padding from the six above):

condition n micro-F1 macro-mAP
ungated 922 0.5510 0.3124
gated β€” nano accepts 443 0.6549 0.5012
what the gate rejected 479 0.4665 0.2302
oracle β€” perfect gate 470 0.6776 0.5086

+0.1888 macro-mAP, a 60% relative gain, against an oracle ceiling of +0.1962 β€” the gate captures 96% of what a perfect gate would deliver. The rejected pile scores 0.2302, so it is discarding the right things. Coverage 48%, gate precision 86%, recall 81%.

This is a refusal, not a repair. It does not fix a bad crop; it tells you not to trust the output and to re-crop. Half the inputs in this simulation get declined.

Gate accuracy on its own task

Held out by source image, so crops of the same garment never straddle the split:

accuracy
nano gate (47K params) 0.8155 Β± 0.0096
cheap baseline 0.6240
majority 0.500

Margin +0.19, winning 3/3 seeds. The cheap baseline is the stronger of a transferred single threshold and a ~66-parameter logistic regression over ten cheap image statistics, both fitted on train and evaluated on test β€” a single threshold alone is too weak a bar to be honest about.

Labels are free and exact: the crop padding is applied by the harness, so the label is the operation performed, not an annotation.

Usage

import numpy as np, onnxruntime as ort
from PIL import Image

so = ort.SessionOptions(); so.intra_op_num_threads = 1
so.add_session_config_entry("session.intra_op.allow_spinning", "0")
gate = ort.InferenceSession("crop_gate.onnx", sess_options=so,
                            providers=["CPUExecutionProvider"])

def usable(pil):
    a = np.asarray(pil.convert("RGB").resize((64, 64), Image.BICUBIC), np.float32) / 255.0
    a = (a - a.mean()) / (a.std() + 1e-6)          # per-image standardisation, as trained
    x = a.transpose(2, 0, 1)[None]
    return gate.run(None, {"image": x})[0].argmax() == 1

if usable(crop):
    attrs = garment_attributes(crop)               # trust the 93.1M model
else:
    ...                                            # re-crop tighter; do not trust the output

Input is RGB, per-image standardised β€” not the SigLIP mean/std normalisation that garment-attributes itself uses. Feeding it SigLIP-normalised tensors will degrade it silently.

Also free: a minimum native-size rule

The gate above judges crop tightness. Crop size is a separate, cheaper check that needs no model at all. Measured on garment-attributes-v2, Fashionpedia val2020, with the padding held at 0.25 and only the crop's native pixel size varying (the model resizes everything to 224x224, so anything below that is upscaled from nothing):

native min-side n macro-mAP
0–24 px 564 0.2316
24–36 px 460 0.4496
36–48 px 523 0.5785
48–64 px 582 0.6223
64–96 px 716 0.6274
96–160 px 771 0.5860
160+ px 1642 0.5069

Below ~24 native pixels the model collapses to a third of its peak. A min(w, h) >= 24 px check keeps 89% of instances while the rejected pile scores 0.2316; >= 48 px keeps 71% and is what the parent's evaluation protocol uses.

There is no upper limit worth imposing. The decline above 96 px is not a resolution effect β€” it is the part-versus-garment split, since large crops are whole garments carrying many attributes and small ones are pockets and collars carrying few. Controlling for instance category, native size does not predict accuracy at all (mean within-category delta +0.0033, positive in 3 of 7 categories).

Nor would higher-resolution input help. 74% of evaluation crops are already upscaled to reach 224Γ—224 and score no worse than crops with more native detail than the model consumes. The model is not starved of pixels; its measured deficit is in rare attributes with few training examples.

Pixels-on-target: which attributes are answerable at all

Crop tightness and crop size both matter, but the quantity that actually governs an attribute is how many pixels its diagnostic feature occupies β€” and that is computable in advance from physical sizes, without asking the model anything.

A button is ~12 mm, a zipper tooth ~5 mm, a pocket opening ~140 mm, a hemline ~1200 mm. Every crop is resized to 224Γ—224, so a feature occupying fraction f of the instance lands on f Γ— 224 pixels no matter what the source resolution was.

pixels-on-target attributes mean AP
0–2 px 13 0.340
2–4 px 23 0.359
4–8 px 10 0.530
20–60 px 80 0.463
60+ px 62 0.584

This is not just a proxy for training data. Support is the stronger driver (correlation +0.526 against +0.302), so the test is whether pixels-on-target still predicts accuracy within support bands. It does, and the effect grows with support:

training support low-pixel AP high-pixel AP delta
100–400 0.334 0.408 +0.074
400–1,500 0.387 0.499 +0.112
1,500+ 0.570 0.714 +0.144

The clearest single case: bead(a) has 2,030 training instances and scores AP 0.133, because a 5 mm bead on a whole garment lands on ~1 pixel. metal has 1,135 instances and scores 0.072. No quantity of training data fixes a feature rendered at one pixel.

The zoom calculation

To put T pixels on a feature of F mm at a 224 px input, crop to a region of F Γ— 224 / T mm. For a 5 mm feature at 8 px, that is a 140 mm crop β€” roughly 8Γ— tighter than a whole-garment crop.

So small-feature attributes are not a resolution problem to be solved by larger inputs; they are a cropping problem. Classify what is visible at garment scale, then crop to sub-regions for the rest. 150 of 202 attributes already exceed 8 px and need no zoom at all.

Prior art: this is Johnson's criteria, rediscovered

The ~8 px threshold above was derived empirically from average-precision curves. It then turned out to match Johnson's criteria (John Johnson, 1958), the standard used in surveillance and thermal optics for over half a century:

task Johnson (1958) measured here
detection β€” something is there 2 px β€”
recognition β€” what kind of thing 8 px ~8 px
identification β€” which specific one 12.8 px β€”

The empirical threshold for "can a CNN classify this attribute" lands on Johnson's recognition value. That convergence is worth stating plainly: the principle is not new, and credit belongs to Johnson.

What is offered here is the application rather than the principle β€” Johnson's criteria describe a human observer detecting or recognising a whole object, whereas the numbers above are a CNN classifying fine-grained attributes of an object it is already looking at, validated per-attribute against training support as a confound. The zoom calculation and the a-priori camera spec follow from that, and the fact that a 1958 optics rule predicts modern CNN behaviour this well is arguably the more useful finding.

Caveat: this fails for absence and gestalt attributes

plain (pattern), no non-textile material and symmetrical are judged over a large area, not by resolving a small part, so a "feature size" is the wrong model for them and they score well despite tiny nominal features. They are excluded from the numbers above and listed in pixels_on_target.json. Identify them by form β€” negations and whole-garment properties β€” not by their results.

Why not just use the model's own confidence?

Because on this task it is worse than rejecting at random.

Selective prediction usually rejects inputs the model is unconfident about. Simulating variably cropped inputs and sweeping coverage, measuring macro-mAP on whatever each method accepts:

method risk–coverage AUC (0.3–1.0) mAP @50% coverage
oracle (true crop padding) 0.2549 0.4312
crop-gate (this model) 0.2540 0.4275
negative entropy 0.2376 0.3715
random rejection 0.2030 0.2995
max-confidence 0.1853 0.2637
top-5 mean confidence 0.1699 0.2377

The gate reaches 99.6% of the oracle. Max-confidence and top-5 confidence both fall below random.

The mechanism is straightforward once seen: fed a full scene, the model finds many things to be confident about and fires attributes across the board. High confidence correlates with a bad crop, not a good one, so confidence-based rejection actively discards the inputs it should keep. This is the miscalibration the selective-prediction literature warns about, in a concrete instance.

The gate avoids it by never consulting the model. It measures a property of the input β€” independently known to predict failure β€” which cannot be corrupted by the model being wrong.

Limitations

  • Binary, and the boundary is a cliff edge chosen from the curve above (between 0.50 and 1.00 padding). Inputs near that boundary are genuinely ambiguous and the gate is least reliable there.
  • 48% coverage in the mixed simulation. If your inputs are already well cropped the gate mostly says yes and buys little; its value scales with how bad your crops are.
  • Trained on Fashionpedia val2020 crops only β€” 3,711 instances from 1,158 images, since only the val split ships bounding boxes. Broader photography is unmeasured.
  • Tightness is not the only failure mode. The garment-attributes card also warns that flat-lay/factory photos are out of domain; this gate does not detect that at all and will happily accept a well-cropped flat-lay.
  • Not a garment detector. It scores a crop you already have; it will not find garments for you.
  • 0.8155 accuracy means roughly one in five decisions is wrong. Use it to triage, not to guarantee.

Relation to the parent model

This does not modify, distil or replace garment-attributes β€” it runs in front of it and costs about 1/2000th as many parameters. The architecture is an ordinary 4-layer conv net and nothing about it is novel. What is offered is the measurement: a published degradation curve, a reproduction of the parent's protocol, and an end-to-end number showing the gate recovers 96% of an oracle's benefit.

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

Collection including resoa/garment-crop-gate-nano