MatteoCargnelutti jimmymendez commited on
Commit
7265e45
·
0 Parent(s):

Squashing commit

Browse files

Co-authored-by: jimmymendez <jimmymendez@users.noreply.huggingface.co>

Files changed (3) hide show
  1. .gitattributes +36 -0
  2. README.md +331 -0
  3. weights/weights.pth +3 -0
.gitattributes ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ weights/best.torchscript filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - efficient-net
4
+ - image-classification
5
+ - document-analysis
6
+ - visual-elements
7
+ - cultural-heritage
8
+ - digitized-books
9
+ datasets:
10
+ - institutional/institutional-books-hl-visual-elements
11
+ pipeline_tag: image-classification
12
+ model-index:
13
+ - name: institutional-books-visual-elements-orientation
14
+ results:
15
+ - task:
16
+ type: image-classification
17
+ name: Visual Element Orientation Classification
18
+ metrics:
19
+ - type: accuracy
20
+ name: Overall accuracy
21
+ value: 0.9134
22
+ dataset:
23
+ type: institutional/institutional-books-hl-visual-elements
24
+ name: Visual elements crops (orientation test split)
25
+ - type: accuracy
26
+ name: upright accuracy
27
+ value: 0.9178
28
+ - type: accuracy
29
+ name: rotate_90_clockwise accuracy
30
+ value: 0.8976
31
+ - type: accuracy
32
+ name: rotate_180 accuracy
33
+ value: 0.9191
34
+ - type: accuracy
35
+ name: rotate_90_counterclockwise accuracy
36
+ value: 0.9191
37
+ license: apache-2.0
38
+ ---
39
+
40
+ # 📚 Institutional Books — Visual Elements Orientation Model (EfficientNetV2-M)
41
+
42
+ A 4‑class image classification model that predicts the rotation correction needed to restore visual elements from digitized book page scans to upright orientation. This model operates on cropped regions (e.g., images, diagrams, ornaments) and is intended as a post-processing stage after visual-element detection.
43
+
44
+ **More information:**
45
+ - 📄 [Full analysis available in our technical report](TODO)
46
+ - 🌐 [Website](https://institutional.org/institutional-books)
47
+
48
+ **See also:**
49
+ - ⚙️ [Pipeline](https://github.com/institutional/institutional-books-visual-elements-pipeline)
50
+ - 🤖 [Detection model](https://huggingface.co/institutional/institutional-books-visual-elements-detection)
51
+ - 🤖 [Classification model](https://huggingface.co/institutional/institutional-books-visual-elements-classification)
52
+
53
+ The Institutional Data Initiative at Harvard Law School Library works with knowledge institutions—from libraries and museums to cultural groups and government agencies—to refine and publish their collections as data. [Reach out to collaborate on your collections](https://institutional.org/).
54
+
55
+ ---
56
+
57
+ ## Outline
58
+
59
+ - [Model Description](#model-description)
60
+ - [Classes](#classes)
61
+ - [Intended Use](#intended-use)
62
+ - [Training](#training)
63
+ - [Dataset](#dataset)
64
+ - [Training Configuration](#training-configuration)
65
+ - [Data Augmentation (Train Only)](#data-augmentation-train-only)
66
+ - [Evaluation](#evaluation)
67
+ - [Per-Epoch Training Summary](#per-epoch-training-summary)
68
+ - [Test Set Performance](#test-set-performance)
69
+ - [Inference Configuration](#inference-configuration)
70
+ - [Usage](#usage)
71
+ - [PyTorch Example](#pytorch-example)
72
+ - [Applying Corrections](#applying-corrections)
73
+ - [Limitations](#limitations)
74
+ - [Citation](#citation)
75
+
76
+ ---
77
+
78
+ ## Model Description
79
+
80
+ - **Architecture:** EfficientNetV2-M
81
+ - **Parameters:** 52,863,480
82
+ - **Classes (corrections):** 4
83
+ - **Input resolution (train/inference):** 480×480 px crops (from 512×512 resize)
84
+ - **Framework:** PyTorch
85
+
86
+ The model predicts the *inverse rotation* required to correct each crop back to upright. Input crops may be in any of four orientations; the model outputs one of four rotation labels.
87
+
88
+ ### Classes
89
+
90
+ The labels are “correction actions” to make the image upright:
91
+
92
+ | Class index | Label | Description |
93
+ |-------------|--------------------------------|-------------------------------------------------------|
94
+ | 0 | **upright** | No rotation needed (already upright) |
95
+ | 1 | **rotate_90_clockwise** | Rotate 90° clockwise to correct |
96
+ | 2 | **rotate_180** | Rotate 180° to correct |
97
+ | 3 | **rotate_90_counterclockwise** | Rotate 90° counter-clockwise to correct |
98
+
99
+ Classes are mutually exclusive.
100
+
101
+ ## Intended Use
102
+
103
+ This model classifies the orientation of **already-detected visual elements** from digitized book pages.
104
+
105
+ **Primary use cases:**
106
+ - Correcting rotations of cropped visual elements in digitization pipelines
107
+ - Normalizing orientation before downstream tasks (captioning, OCR on musical scores, layout analysis)
108
+ - Quality control on large-scale digitized collections (flagging mis-rotate elements)
109
+
110
+ **Out of scope:**
111
+ - General full-page orientation detection (expects tight crops of a single element)
112
+ - Classification of semantic content (e.g., “image vs. music”)
113
+ - Arbitrary-angle rotation beyond multiples of 90°
114
+
115
+ ## Training
116
+
117
+ ### Dataset
118
+
119
+ Source data consists of **7,904** manually curated crops of visual elements from the [Institutional Books collection](https://huggingface.co/collections/institutional/institutional-books).
120
+
121
+ Original (pre-synthetic) label distribution (estimated, by source orientation):
122
+
123
+ - **upright:** 93.0%
124
+ - **rotate_90_clockwise:** 5.6%
125
+ - **rotate_90_counterclockwise:** 1.3%
126
+ - **rotate_180:** 0.03%
127
+
128
+ To avoid this extreme imbalance and to directly learn the correction operation:
129
+
130
+ 1. All images were first **manually corrected to upright**.
131
+ 2. Each upright image was **synthetically rotated** by 0°, 90°, 180°, and 270°.
132
+ 3. The target label is the **inverse rotation** needed to restore the upright orientation.
133
+
134
+ Resulting synthetic orientation dataset:
135
+
136
+ - **Total samples (with synthetic rotations):** 31,616
137
+ - **Train samples:** 25,292
138
+ - **Val samples:** 3,160
139
+ - **Test samples:** 3,164
140
+ - **Split:** 0.8 / 0.1 / 0.1 (train / val / test)
141
+
142
+ Each original crop contributes four synthetic samples (one per orientation), producing a **balanced label distribution** across the four classes in the synthetic set.
143
+
144
+ ### Training Configuration
145
+
146
+ | Parameter | Value |
147
+ |------------------------|----------------------------------------|
148
+ | Backbone | EfficientNetV2-M |
149
+ | Classifier head | Dropout(p=0.3) → Linear(1280, 4) |
150
+ | Image size (train) | Resize(512×512) → RandomCrop(480×480) |
151
+ | Image size (val/test) | Resize(512×512) → CenterCrop(480×480) |
152
+ | Batch size | 32 |
153
+ | Max epochs | 20 |
154
+ | Optimizer / LR | Not specified (standard schedule) |
155
+ | Normalization | ImageNet mean/std |
156
+ | Hardware | Single NVIDIA GH200 GPU |
157
+ | Total training time | 58 min 13 sec (20 epochs) |
158
+ | Avg. per epoch | ~2 min 55 sec |
159
+ | Train samples/epoch | 25,292 (~791 steps/epoch) |
160
+ | Throughput | ~145 images/sec |
161
+
162
+ ### Data Augmentation (Train Only)
163
+
164
+ Preprocessing:
165
+
166
+ - `Resize(512×512)`
167
+ - `RandomCrop(480×480)`
168
+ - `ColorJitter(brightness=0.2, contrast=0.2, saturation=0.1)`
169
+
170
+ Stochastic augmentations:
171
+
172
+ | Augmentation | Implementation | Probability |
173
+ |---------------------|-----------------------------------|-------------|
174
+ | Random auto-contrast| `RandomAutocontrast` | p = 0.3 |
175
+ | Random invert | `RandomInvert` | p = 0.15 |
176
+ | Random grayscale | `RandomGrayscale` | p = 0.2 |
177
+ | Gaussian blur | `GaussianBlur(k=5, σ=0.1–2.0)` | p = 1.0 |
178
+ | Random erasing | `RandomErasing(scale=0.02–0.15)` | p = 0.2 |
179
+
180
+ Validation/Test preprocessing:
181
+
182
+ - `Resize(512×512)`
183
+ - `CenterCrop(480×480)`
184
+ - `Normalize` (ImageNet stats)
185
+
186
+ ## Evaluation
187
+
188
+ ### Per-Epoch Training Summary
189
+
190
+ On the validation set, accuracy increases steadily and plateaus around **90%**, while validation loss begins to rise after approximately epoch 10, indicating **moderate overfitting**. Heavy augmentations successfully limit overfitting enough that the **held-out test set slightly outperforms validation**.
191
+
192
+ Final epoch (20):
193
+
194
+ - **Train accuracy:** 99.81%
195
+ - **Val accuracy:** 90.35%
196
+ - **Train loss:** 0.0058
197
+ - **Val loss:** 0.4753
198
+
199
+ ### Test Set Performance
200
+
201
+ On the held-out test set (3,164 samples; 791 per class):
202
+
203
+ - **Overall accuracy:** 91.34% (2,890 / 3,164)
204
+
205
+ Per-class accuracy:
206
+
207
+ | Class | Accuracy | Correct / Total |
208
+ |------------------------------|----------|-----------------|
209
+ | **upright** | 91.78% | 726 / 791 |
210
+ | **rotate_90_clockwise** | 89.76% | 710 / 791 |
211
+ | **rotate_180** | 91.91% | 727 / 791 |
212
+ | **rotate_90_counterclockwise** | 91.91% | 727 / 791 |
213
+
214
+ Additional notes:
215
+
216
+ - **Misclassifications:** 274 of the 3,164 test samples are misclassified (2,890 correct → 91.34% overall accuracy). These break down per class as 65 (`upright`), 81 (`rotate_90_clockwise`), 64 (`rotate_180`), and 64 (`rotate_90_counterclockwise`).
217
+ - The 90° clockwise class is the most challenging, but still achieves close to 90% accuracy.
218
+
219
+ ## Inference Configuration
220
+
221
+ Typical inference settings:
222
+
223
+ | Parameter | Value |
224
+ |-----------------|-----------------------------------|
225
+ | Image size | 512×512 resize → 480×480 center crop |
226
+ | Batch size | 32 (tune for available GPU memory) |
227
+ | Normalization | ImageNet mean/std |
228
+ | Output | 4-way softmax over orientation labels |
229
+
230
+ The top-1 prediction corresponds to the **rotation to apply** to make the crop upright.
231
+
232
+ ## Usage
233
+
234
+ ### PyTorch Example
235
+
236
+ ```python
237
+ import torch
238
+ import torch.nn as nn
239
+ import torchvision.models as models
240
+ from torchvision import transforms
241
+ from huggingface_hub import hf_hub_download
242
+ from PIL import Image
243
+
244
+ # Download weights (the repo ships a state_dict at weights/weights.pth)
245
+ model_path = hf_hub_download(
246
+ repo_id="institutional/institutional-books-visual-elements-orientation",
247
+ filename="weights/weights.pth",
248
+ )
249
+
250
+ # Build the architecture and load the state_dict
251
+ model = models.efficientnet_v2_m(weights=None)
252
+ num_features = model.classifier[1].in_features
253
+ model.classifier = nn.Sequential(
254
+ nn.Dropout(p=0.3, inplace=True),
255
+ nn.Linear(num_features, 4),
256
+ )
257
+ state_dict = torch.load(model_path, map_location="cuda", weights_only=True)
258
+ model.load_state_dict(state_dict)
259
+ model.to("cuda")
260
+ model.eval()
261
+
262
+ # Preprocessing: match validation/test pipeline
263
+ preprocess = transforms.Compose([
264
+ transforms.Resize((512, 512)),
265
+ transforms.CenterCrop(480),
266
+ transforms.ToTensor(),
267
+ transforms.Normalize(
268
+ mean=[0.485, 0.456, 0.406], # ImageNet
269
+ std=[0.229, 0.224, 0.225],
270
+ ),
271
+ ])
272
+
273
+ idx_to_label = {
274
+ 0: "upright",
275
+ 1: "rotate_90_clockwise",
276
+ 2: "rotate_180",
277
+ 3: "rotate_90_counterclockwise",
278
+ }
279
+
280
+ # A high confidence threshold (0.99) is applied: predictions below it
281
+ # default to "upright" to minimize false corrections.
282
+ CONFIDENCE_THRESHOLD = 0.99
283
+
284
+ def predict_orientation(path):
285
+ img = Image.open(path).convert("RGB")
286
+ x = preprocess(img).unsqueeze(0).to("cuda")
287
+ with torch.no_grad():
288
+ logits = model(x)
289
+ probs = torch.softmax(logits, dim=1)[0]
290
+ top1 = int(torch.argmax(probs))
291
+ conf = float(probs[top1])
292
+ label = idx_to_label[top1] if conf >= CONFIDENCE_THRESHOLD else "upright"
293
+ return label, conf, probs.cpu().tolist()
294
+
295
+ label, conf, all_probs = predict_orientation("crop.jpg")
296
+ print(f"Predicted correction: {label}, confidence: {conf:.3f}")
297
+ ```
298
+
299
+ ### Applying Corrections
300
+
301
+ ```python
302
+ from PIL import Image
303
+
304
+ def apply_correction(img, label):
305
+ if label == "upright":
306
+ return img
307
+ elif label == "rotate_90_clockwise":
308
+ return img.rotate(-90, expand=True)
309
+ elif label == "rotate_180":
310
+ return img.rotate(180, expand=True)
311
+ elif label == "rotate_90_counterclockwise":
312
+ return img.rotate(90, expand=True)
313
+ else:
314
+ raise ValueError(f"Unknown label: {label}")
315
+ ```
316
+
317
+ ## Limitations
318
+
319
+ - Trained specifically on crops from the [Institutional Books collection](https://huggingface.co/collections/institutional/institutional-books). Performance may degrade on:
320
+ - Non-book imagery
321
+ - Heavily stylized or abstract content
322
+ - Very low-resolution or heavily compressed scans
323
+ - Supports only **multiples of 90°** rotations; does not handle slight skews or arbitrary angle rotations.
324
+ - Expected to work best when:
325
+ - Crops contain a clear visual object/structure
326
+ - Background is not overwhelmingly dominant
327
+ - Model assumes images are RGB; grayscale images are internally handled via standard preprocessing but not natively optimized for non-RGB channels.
328
+
329
+ ## Citation
330
+
331
+ > Citation forthcoming.
weights/weights.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ca769dd1135b03e6873ee953b408e460f3cfab909fd45bc78099b4e398670528
3
+ size 213069763