kiyo07 commited on
Commit
d469c19
·
verified ·
1 Parent(s): c320034

Store inference weights as safetensors with a strict direct loader

Browse files
LICENSING.md CHANGED
@@ -2,8 +2,8 @@
2
 
3
  | Artifact | License |
4
  | --- | --- |
5
- | `v3.pth` model weights | Apache-2.0; full text in `LICENSE`, with upstream attribution in `NOTICE`. |
6
- | `predict.py` and `inference_engine.py` | AGPL-3.0-only; full text in `LICENSE-CODE-AGPL-3.0.txt`. These files are the complete release program source. |
7
  | Release documentation | Apache-2.0, except third-party notices and license texts, which retain their terms. |
8
  | Public dataset material | Not redistributed. Uploader-listed CC BY 4.0; credits/modification notes are retained in README and NOTICE, with license text in `LICENSE-CC-BY-4.0.txt`. |
9
  | Separately installed dependencies | Their own licenses and notices. No dependency binaries or environments are bundled. |
@@ -22,7 +22,7 @@ AGPL permits commercial activity when its terms are met. A proprietary integrati
22
 
23
  ## Source and installation
24
 
25
- Both Python source files, pinned requirements, full license texts and installation/inference commands are included in this package. `requirements-lock-macos-py313.txt` records the tested dependency resolution. No private local module, training records or drawings are needed to run the CLI. `SHA256SUMS` verifies the package files.
26
 
27
  Unmodified dependencies are obtained from their publishers. Exact releases include [PyMuPDF 2.0](https://pypi.org/project/PyMuPDF/2.0/#files) ([source](https://github.com/pymupdf/PyMuPDF), [MuPDF](https://mupdf.com/releases)) and [RF-DETR 1.11.2](https://pypi.org/project/rfdetr/1.11.2/#files) ([source](https://github.com/roboflow/rf-detr)). Preserve matching source/notices when conveying dependencies. This repository is not a binary-distribution compliance bundle.
28
 
 
2
 
3
  | Artifact | License |
4
  | --- | --- |
5
+ | `model.safetensors` model weights | Apache-2.0; full text in `LICENSE`, with upstream attribution in `NOTICE`. |
6
+ | `predict.py`, `inference_engine.py`, and `model_loader.py` | AGPL-3.0-only; full text in `LICENSE-CODE-AGPL-3.0.txt`. These files are the complete release program source. |
7
  | Release documentation | Apache-2.0, except third-party notices and license texts, which retain their terms. |
8
  | Public dataset material | Not redistributed. Uploader-listed CC BY 4.0; credits/modification notes are retained in README and NOTICE, with license text in `LICENSE-CC-BY-4.0.txt`. |
9
  | Separately installed dependencies | Their own licenses and notices. No dependency binaries or environments are bundled. |
 
22
 
23
  ## Source and installation
24
 
25
+ All three Python source files, pinned requirements, full license texts and installation/inference commands are included in this package. `requirements-lock-macos-py313.txt` records the tested dependency resolution. No private local module, training records or drawings are needed to run the CLI. `SHA256SUMS` verifies the package files.
26
 
27
  Unmodified dependencies are obtained from their publishers. Exact releases include [PyMuPDF 2.0](https://pypi.org/project/PyMuPDF/2.0/#files) ([source](https://github.com/pymupdf/PyMuPDF), [MuPDF](https://mupdf.com/releases)) and [RF-DETR 1.11.2](https://pypi.org/project/rfdetr/1.11.2/#files) ([source](https://github.com/roboflow/rf-detr)). Preserve matching source/notices when conveying dependencies. This repository is not a binary-distribution compliance bundle.
28
 
NOTICE CHANGED
@@ -10,7 +10,9 @@ https://storage.googleapis.com/rfdetr/small_coco/checkpoint_best_regular.pth
10
  https://roboflow.com/licensing
11
 
12
  Changes: the native base was fine-tuned for door openings. The release checkpoint
13
- retains inference tensors while omitting training records/identifying metadata.
 
 
14
  The release CLI adapts the existing inference implementation for portable use.
15
  RF-DETR acknowledges LW-DETR, DINOv2 and Deformable DETR in its distribution.
16
  No standalone upstream NOTICE was present in the installed RF-DETR distribution;
 
10
  https://roboflow.com/licensing
11
 
12
  Changes: the native base was fine-tuned for door openings. The release checkpoint
13
+ retains inference tensor names, shapes, dtypes and values in safetensors while
14
+ omitting training records/identifying metadata. Packaging conversion does not
15
+ retrain the model or change its inference preprocessing.
16
  The release CLI adapts the existing inference implementation for portable use.
17
  RF-DETR acknowledges LW-DETR, DINOv2 and Deformable DETR in its distribution.
18
  No standalone upstream NOTICE was present in the installed RF-DETR distribution;
README.md CHANGED
@@ -21,7 +21,7 @@ Fine-tuned using public floor-plan datasets and additional architectural drawing
21
 
22
  One opening counts as one item; double doors should count as one opening. The single foreground class is `door` (ID 0). Windows and cabinets are outside the intended class. Predictions require human review, including areas where no box was proposed. Counts are preliminary and do not automatically identify unique physical openings across repeated drawings.
23
 
24
- **License scope:** `v3.pth` model weights are Apache-2.0. The supplied Python runner, including PDF functionality, is AGPL-3.0-only. See [LICENSING.md](LICENSING.md) and [NOTICE](NOTICE).
25
 
26
  ## Install
27
 
@@ -34,7 +34,7 @@ python -m pip install -r requirements.txt
34
  python -m pip check
35
  ```
36
 
37
- `requirements-lock-macos-py313.txt` provides the complete tested dependency resolution for that platform. Training and web-server extras are not required. This is a native `rfdetr==1.11.2` PyTorch checkpoint, not a Transformers `from_pretrained` bundle.
38
 
39
  ## Predict
40
 
@@ -51,7 +51,7 @@ python predict.py --input plan.pdf --pages 1 --output results-pdf --device cpu
51
  python predict.py --input plan.pdf --pages 2,4-6 --output results-selected --device cpu
52
  ```
53
 
54
- Each output directory must be new. `v3.pth` is selected relative to the script by default. Apple Silicon users can use `--device mps`. `--device cuda` requires a compatible PyTorch/CUDA installation; CUDA and other operating systems have not been validated for this release.
55
 
56
  Optional drawing-area crop and render preview:
57
 
@@ -63,7 +63,21 @@ python predict.py --help
63
 
64
  ROI bounds are left/top/right/bottom percentages. Image loading applies EXIF orientation, composites transparency onto white, and converts to RGB. PDFs are rendered in RGB at **150 DPI** by default, with a 40-million-pixel page limit.
65
 
66
- Inference uses **768×768** white-padded tiles with **384-pixel stride**, model resolution **512×512**, confidence threshold **0.3**, tile-boundary rechecks, and final greedy NMS with IoU **0.5**. The model constructor is `RFDETRSmall(pretrain_weights=..., num_classes=1, resolution=512, device=...)`. The existing box/score handling is preserved without adding a class filter. Override settings with `--dpi`, `--threshold`, `--nms-iou`, or `--checkpoint`; changing settings can change predictions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  ## Outputs and limitations
69
 
 
21
 
22
  One opening counts as one item; double doors should count as one opening. The single foreground class is `door` (ID 0). Windows and cabinets are outside the intended class. Predictions require human review, including areas where no box was proposed. Counts are preliminary and do not automatically identify unique physical openings across repeated drawings.
23
 
24
+ **License scope:** `model.safetensors` model weights are Apache-2.0. The supplied Python runner, including PDF functionality, is AGPL-3.0-only. See [LICENSING.md](LICENSING.md) and [NOTICE](NOTICE).
25
 
26
  ## Install
27
 
 
34
  python -m pip check
35
  ```
36
 
37
+ `requirements-lock-macos-py313.txt` provides the complete tested dependency resolution for that platform. Training and web-server extras are not required. The weights are stored in safetensors and loaded directly by `model_loader.py` with the pinned `rfdetr==1.11.2` implementation. No `.pth` file, pickle loading, conversion back to pickle, or base-weight download is required at runtime. This package does not implement or claim Transformers `pipeline()` / `from_pretrained()` compatibility.
38
 
39
  ## Predict
40
 
 
51
  python predict.py --input plan.pdf --pages 2,4-6 --output results-selected --device cpu
52
  ```
53
 
54
+ Each output directory must be new. `model.safetensors` is selected relative to the script by default. Apple Silicon users can use `--device mps`. `--device cuda` requires a compatible PyTorch/CUDA installation; CUDA and other operating systems have not been validated for this release.
55
 
56
  Optional drawing-area crop and render preview:
57
 
 
63
 
64
  ROI bounds are left/top/right/bottom percentages. Image loading applies EXIF orientation, composites transparency onto white, and converts to RGB. PDFs are rendered in RGB at **150 DPI** by default, with a 40-million-pixel page limit.
65
 
66
+ Inference uses **768×768** white-padded tiles with **384-pixel stride**, model resolution **512×512**, confidence threshold **0.3**, tile-boundary rechecks, and final greedy NMS with IoU **0.5**. The direct loader constructs `RFDETRSmall(pretrain_weights=None, num_classes=1, resolution=512, device=...)`, strictly loads the included safetensors state, restores class names, and uses the existing RF-DETR prediction facade. The existing box/score handling is preserved without adding a class filter. Override settings with `--dpi`, `--threshold`, `--nms-iou`, `--checkpoint` (a safetensors file), or `--model-config` (defaults beside weights); changing settings can change predictions.
67
+
68
+ ## Direct loader
69
+
70
+ `model_loader.py` reads `model_config.json` and `model.safetensors` directly. The minimal config contains only the pinned RF-DETR version, Small variant, resolution, foreground class count and class mapping. Architecture defaults are fixed by `rfdetr==1.11.2`: a windowed DINOv2 Small encoder, three decoder layers, patch size 16, positional grid 32 and 300 queries / 13 groups. The loader rejects unsupported config/version, missing or unexpected weight keys, and incompatible shapes or dtypes. It uses strict state loading and confirms the loaded tensors match the file. The model runs without optimization, quantization, or precision conversion.
71
+
72
+ The image commands above use this loader together with the unchanged image preprocessing, tiling, boundary rechecks and NMS in `inference_engine.py`. A minimal Python loading example is:
73
+
74
+ ```python
75
+ from model_loader import load_model
76
+
77
+ model = load_model(device="cpu") # model.safetensors + model_config.json beside loader
78
+ ```
79
+
80
+ Use `predict.py` for image/PDF prediction with the complete preprocessing shown above. Initializing the model without pretrained weights creates its architecture locally; every required state tensor is then supplied by the strict safetensors load. The file preserves the original tensor names, shapes, dtypes and values, including its empty boolean buffer. No keys are dropped to deduplicate shared storage.
81
 
82
  ## Outputs and limitations
83
 
SHA256SUMS CHANGED
@@ -1,11 +1,13 @@
1
  8cabf52c61130fc97a7e5da5e3685ccf44841c0c53d6f881189369b136603cca LICENSE
2
  9ba9550ad48438d0836ddab3da480b3b69ffa0aac7b7878b5a0039e7ab429411 LICENSE-CC-BY-4.0.txt
3
  57c8ff33c9c0cfc3ef00e650a1cc910d7ee479a8bc509f6c9209a7c2a11399d6 LICENSE-CODE-AGPL-3.0.txt
4
- f40718c0158ad8eaff3ef127571c151035966b44b5f70ddf4b3f4adddd45430d LICENSING.md
5
- 0dfdf3ee9cdbe6061163b5d0812329f1d0a133014095611fdcb0558be7a016bb NOTICE
6
- 0b55369b068e12ed6157294c3923e801c5081593f81899e6ff1cbc808ff4bcfd README.md
7
- 90becba403ec439e4825c15f5aa390464f1b174169301cd652175be51a79c63d inference_engine.py
 
 
 
8
  e904d946b83504016bf6b83a452b8959212e07245c58424b5fccdeac7daffafb predict.py
9
  6828f00d1c7922c5f3b7af01708e65b19581f38a0929b27d7e521b9ff66ab77b requirements-lock-macos-py313.txt
10
- a212fdb899ea5fbea8a0da96a0c3618f7ca693f89ab59117c7436c3c94ecf618 requirements.txt
11
- 518e463dfc63c4ebcac82b7719f82a105c2e8c2e3461326933996cbc8becd0b3 v3.pth
 
1
  8cabf52c61130fc97a7e5da5e3685ccf44841c0c53d6f881189369b136603cca LICENSE
2
  9ba9550ad48438d0836ddab3da480b3b69ffa0aac7b7878b5a0039e7ab429411 LICENSE-CC-BY-4.0.txt
3
  57c8ff33c9c0cfc3ef00e650a1cc910d7ee479a8bc509f6c9209a7c2a11399d6 LICENSE-CODE-AGPL-3.0.txt
4
+ 39c8f97aa86f61a2d47d13cd1aac0667333fff03339421fefede6bb437be739a LICENSING.md
5
+ e254f895fbdbe26efea3aa2a9d07f0c19d2e6c0fd4b5ec09367cd52816ee88fe NOTICE
6
+ 3457211166e298fbdbd7f790727a292786d7741c60776f9373ce7836264727cb README.md
7
+ 01a1c9a923de14a8af1b36078121328182db7208ae76e9664f742b6f0a22725c inference_engine.py
8
+ aca8b59ff9c6f144a1ca284f689683e9ed7cd951555832cfc09c10e5ae93bd0d model.safetensors
9
+ 4b143322ca27af276eb497a959009b14729accf70d2bfaee98e751a8073b9443 model_config.json
10
+ ec26695f9ba3bc72d4b9bd48414183af3ef5ccc6f03c473c26f12844cc25b308 model_loader.py
11
  e904d946b83504016bf6b83a452b8959212e07245c58424b5fccdeac7daffafb predict.py
12
  6828f00d1c7922c5f3b7af01708e65b19581f38a0929b27d7e521b9ff66ab77b requirements-lock-macos-py313.txt
13
+ 5f29cba77ad563b6fe076b2364afc921894bad2bad0617529c9f85e71746cd26 requirements.txt
 
inference_engine.py CHANGED
@@ -198,7 +198,8 @@ def main():
198
  parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
199
  parser.add_argument('--input', required=True, type=Path, help='PDF or one PNG/JPEG image.')
200
  parser.add_argument('--pages', help='Required for PDFs: 1-based page numbers, e.g. 5 or 5,7-9.')
201
- parser.add_argument('--checkpoint', type=Path, default=Path(__file__).resolve().parent / 'v3.pth')
 
202
  parser.add_argument('--output', type=Path, help='New output folder; defaults to a timestamped outputs/ folder.')
203
  parser.add_argument('--dpi', type=int, default=150, help='PDF rendering DPI. Default: 150, matching training preparation.')
204
  parser.add_argument('--threshold', type=float, default=.3)
@@ -257,14 +258,13 @@ def main():
257
  if installed != '1.11.2':
258
  parser.error(f'This script requires RF-DETR 1.11.2; found {installed}.')
259
  import torch
260
- from rfdetr import RFDETRSmall
261
  if args.device == 'mps' and not torch.backends.mps.is_available():
262
  parser.error('MPS unavailable; use the Mac environment or pass --device cpu.')
263
  if args.device == 'cuda' and not torch.cuda.is_available():
264
  parser.error('CUDA unavailable.')
265
- print('Loading v3 door checkpoint...', flush=True)
266
- model = RFDETRSmall(pretrain_weights=str(args.checkpoint.resolve()), num_classes=1,
267
- resolution=512, device=args.device)
268
  output.mkdir(parents=True)
269
  report = dict(input=args.input.name, checkpoint=None if args.preview_only else args.checkpoint.name,
270
  status='running', preview_only=args.preview_only, threshold=args.threshold, nms_iou=args.nms_iou,
 
198
  parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
199
  parser.add_argument('--input', required=True, type=Path, help='PDF or one PNG/JPEG image.')
200
  parser.add_argument('--pages', help='Required for PDFs: 1-based page numbers, e.g. 5 or 5,7-9.')
201
+ parser.add_argument('--checkpoint', type=Path, default=Path(__file__).resolve().parent / 'model.safetensors')
202
+ parser.add_argument('--model-config', type=Path, help='Architecture JSON; defaults beside weights.')
203
  parser.add_argument('--output', type=Path, help='New output folder; defaults to a timestamped outputs/ folder.')
204
  parser.add_argument('--dpi', type=int, default=150, help='PDF rendering DPI. Default: 150, matching training preparation.')
205
  parser.add_argument('--threshold', type=float, default=.3)
 
258
  if installed != '1.11.2':
259
  parser.error(f'This script requires RF-DETR 1.11.2; found {installed}.')
260
  import torch
261
+ from model_loader import load_model
262
  if args.device == 'mps' and not torch.backends.mps.is_available():
263
  parser.error('MPS unavailable; use the Mac environment or pass --device cpu.')
264
  if args.device == 'cuda' and not torch.cuda.is_available():
265
  parser.error('CUDA unavailable.')
266
+ print('Loading safetensors door weights...', flush=True)
267
+ model = load_model(args.checkpoint, args.model_config, device=args.device)
 
268
  output.mkdir(parents=True)
269
  report = dict(input=args.input.name, checkpoint=None if args.preview_only else args.checkpoint.name,
270
  status='running', preview_only=args.preview_only, threshold=args.threshold, nms_iou=args.nms_iou,
v3.pth → model.safetensors RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:518e463dfc63c4ebcac82b7719f82a105c2e8c2e3461326933996cbc8becd0b3
3
- size 127326537
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aca8b59ff9c6f144a1ca284f689683e9ed7cd951555832cfc09c10e5ae93bd0d
3
+ size 127223664
model_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "variant": "RFDETRSmall",
3
+ "rfdetr_version": "1.11.2",
4
+ "resolution": 512,
5
+ "num_classes": 1,
6
+ "id2label": {
7
+ "0": "door"
8
+ }
9
+ }
model_loader.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: AGPL-3.0-only
2
+ # Safetensors loading added 2026-10-28; inference preprocessing is unchanged.
3
+ """Strict, direct safetensors loader for this pinned RF-DETR Small release."""
4
+ from importlib.metadata import version
5
+ import json
6
+ from pathlib import Path
7
+ import warnings
8
+
9
+ import torch
10
+ from safetensors.torch import load_file
11
+
12
+ HERE = Path(__file__).resolve().parent
13
+
14
+
15
+ def load_model(weights_path=None, config_path=None, device='cpu'):
16
+ """Rebuild Small, load every tensor strictly, and return its RF-DETR facade.
17
+
18
+ No checkpoint deserialization, temporary pickle, pretrained download, dtype
19
+ conversion, or inference optimization is used. The CLI supplies the existing
20
+ RGB preparation, tiling, boundary rechecks and NMS via inference_engine.py.
21
+ """
22
+ weights_path = Path(weights_path) if weights_path is not None else HERE / 'model.safetensors'
23
+ config_path = Path(config_path) if config_path is not None else weights_path.parent / 'model_config.json'
24
+ if weights_path.suffix != '.safetensors':
25
+ raise ValueError('Use a .safetensors weights file.')
26
+ config = json.loads(config_path.read_text())
27
+ expected_config = {
28
+ 'variant': 'RFDETRSmall', 'rfdetr_version': '1.11.2',
29
+ 'resolution': 512, 'num_classes': 1, 'id2label': {'0': 'door'},
30
+ }
31
+ if config != expected_config:
32
+ raise ValueError('model_config.json does not describe this RF-DETR Small release.')
33
+ if version('rfdetr') != config['rfdetr_version']:
34
+ raise RuntimeError('Install the pinned rfdetr==1.11.2 inference dependencies.')
35
+ if device not in ('cpu', 'mps', 'cuda'):
36
+ raise ValueError('Choose cpu, mps or cuda.')
37
+ if device == 'mps' and not torch.backends.mps.is_available():
38
+ raise RuntimeError('MPS unavailable; use --device cpu.')
39
+ if device == 'cuda' and not torch.cuda.is_available():
40
+ raise RuntimeError('CUDA unavailable; use --device cpu.')
41
+
42
+ from rfdetr import RFDETRSmall
43
+ from rfdetr.config import PretrainWeightsCompatibilityWarning
44
+
45
+ # The pinned Small constructor uses bundled backbone configuration. Patch 16
46
+ # and positional grid 32 create the backbone locally, without DINOv2 weights.
47
+ # Its expected "initialised from scratch" warning is superseded by the strict
48
+ # complete-state load below; no randomly initialized weights remain in use.
49
+ with warnings.catch_warnings():
50
+ warnings.simplefilter('ignore', PretrainWeightsCompatibilityWarning)
51
+ model = RFDETRSmall(pretrain_weights=None, num_classes=config['num_classes'],
52
+ resolution=config['resolution'], device=device)
53
+ module = model.model.model
54
+ state = load_file(str(weights_path), device='cpu')
55
+ expected = module.state_dict()
56
+ missing = sorted(expected.keys() - state.keys())
57
+ unexpected = sorted(state.keys() - expected.keys())
58
+ if missing or unexpected:
59
+ raise RuntimeError(f'Weight key mismatch: missing={missing}, unexpected={unexpected}')
60
+ for name, tensor in state.items():
61
+ if tensor.shape != expected[name].shape or tensor.dtype != expected[name].dtype:
62
+ raise RuntimeError(f'Weight shape/dtype mismatch: {name}')
63
+ module.load_state_dict(state, strict=True)
64
+ loaded_state = module.state_dict()
65
+ if not all(torch.equal(tensor, loaded_state[name]) for name, tensor in state.items()):
66
+ raise RuntimeError('Loaded model tensors do not exactly match the safetensors file.')
67
+ module.eval()
68
+ model.model.class_names = [config['id2label'][str(i)] for i in range(config['num_classes'])]
69
+ model.model.args.class_names = model.model.class_names
70
+ return model
requirements.txt CHANGED
@@ -8,3 +8,4 @@ pymupdf==2.0
8
  supervision==0.30.8
9
  transformers==5.19.0
10
  huggingface_hub==1.33.0
 
 
8
  supervision==0.30.8
9
  transformers==5.19.0
10
  huggingface_hub==1.33.0
11
+ safetensors==0.8.0