File size: 7,324 Bytes
e944460 3c2e84a e944460 a50e244 e944460 3c2e84a e944460 a50e244 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | ---
license: cc-by-nc-4.0
pipeline_tag: keypoint-detection
tags:
- ruler-reading
- image-scale-estimation
- computer-vision
- onnx
---
# RulerNet
RulerNet estimates image scale from a visible ruler by detecting centimeter
marks and fitting them with a geometric progression. It is designed to remain
robust when a ruler is rotated, viewed in perspective, or partially occluded.
This repository accompanies **“RulerNet: Learning Perspective-Invariant Ruler
Representations for Robust Image Scale Estimation,”** published in
*Computerized Medical Imaging and Graphics*. Paper: [RulerNet: Learning Perspective-Invariant Ruler Representations for Robust Image Scale Estimation](https://huggingface.co/papers/2507.07077).
Try the interactive CPU demo: [RulerNet-Demo](https://huggingface.co/spaces/ymp5078/RulerNet-Demo).
For training and evaluation code, see the [GitHub repository](https://github.com/ymp5078/RulerNet).
## Repository contents
| Location | Contents | Use |
| --- | --- | --- |
| `data/AnyRuler.zip` | AnyRuler images and centimeter-mark annotations (998 MB) | Training and testing |
| `data/Rulers2023_scale.zip` | Rulers2023 images and centimeter-mark annotations (2.46 GB) | Evaluation |
| `weights/final_rulernet.zip` | Final RulerNet checkpoint (131 MB) | PyTorch inference, evaluation, or fine-tuning |
| `weights/final_deepgp.zip` | DeepGP solver checkpoint (112 MB) | Optional faster geometric-progression solving |
| `weights/pretrained_rulernet.zip` | Synthetic-data pretrained RulerNet checkpoint (158 MB) | Initialize training to reproduce the paper setup |
| `model.onnx` | CPU-ready ONNX export (57.1 MB) | Lightweight deployment and inference |
## Download files
Install the Hugging Face Hub client:
```bash
pip install -U huggingface_hub
```
Download individual archives with the CLI:
```bash
hf download ymp5078/RulerNet data/AnyRuler.zip --local-dir .
hf download ymp5078/RulerNet data/Rulers2023_scale.zip --local-dir .
hf download ymp5078/RulerNet weights/final_rulernet.zip --local-dir .
hf download ymp5078/RulerNet weights/final_deepgp.zip --local-dir .
hf download ymp5078/RulerNet weights/pretrained_rulernet.zip --local-dir .
hf download ymp5078/RulerNet model.onnx --local-dir .
```
Extract an archive before using it:
```bash
unzip data/AnyRuler.zip -d data/
unzip weights/final_rulernet.zip -d weights/
```
## Datasets
### AnyRuler
`AnyRuler.zip` contains 1,416 annotated ruler images. Use it for training or
for evaluating a model with centimeter-mark labels. After extraction, provide
the extracted directory to the code repository with `--data-dir`.
```text
<data-dir>/
├── ruler_image/ # input images
└── cm_marks/ # matching JSON centimeter-mark annotations
```
### Rulers2023
`Rulers2023_scale.zip` contains the Rulers2023 evaluation images together
with centimeter-mark annotations. Use it with `--test-dataset ruler2023`.
```text
<data-dir>/
├── real-test/images/ # evaluation images
└── real-test-marks/ # JSON centimeter-mark annotations
```
## PyTorch checkpoints
Clone the code repository and install its dependencies before using the
checkpoints:
```bash
git clone https://github.com/ymp5078/RulerNet.git
cd RulerNet
pip install -r requirements.txt
```
Use the final RulerNet checkpoint for inference:
```bash
python inference.py \
--config configs/config_graphic_gen.yaml \
--checkpoint <path-to-final_rulernet>/checkpoints/epoch=199-step=20000.ckpt \
--img-size 768 768 \
--ruler-mode optimize \
--image-path <image-or-directory> \
--result-dir <output-directory>
```
Append the following option to the inference or evaluation command to use the
optional learned DeepGP geometric-progression solver:
```bash
--gp-solver-path <path-to-final_deepgp>/checkpoints/epoch=999-step=1200000.ckpt
```
To reproduce the pretraining initialization used in the paper, start training
from the checkpoint in `pretrained_rulernet.zip`:
```bash
python main.py \
--config configs/config_pretrain.yaml \
--data-dir <anyruler-data-dir> \
--checkpoint <path-to-pretrained_rulernet>/checkpoints/epoch=79-step=128240.ckpt
```
The synthetic-ruler images used for pretraining are reproducible with
[`sdxl_inference.py`](https://github.com/ymp5078/RulerNet/blob/main/sdxl_inference.py);
they are not distributed as a separate archive.
## ONNX inference
`model.onnx` is the CPU-ready export used by the [interactive demo](https://huggingface.co/spaces/ymp5078/RulerNet-Demo).
It expects a float32 tensor named `input` with shape **`(1, 3, 768, 768)`**:
an RGB image scaled to `[0, 1]`, resized while preserving aspect ratio, and
zero-padded to 768 × 768.
Install the lightweight runtime:
```bash
pip install -U huggingface_hub onnxruntime numpy pillow
```
The following example downloads the model, prepares an image exactly as in the
demo, and runs inference on CPU:
```python
import numpy as np
import onnxruntime as ort
from huggingface_hub import hf_hub_download
from PIL import Image
model_path = hf_hub_download(repo_id="ymp5078/RulerNet", filename="model.onnx")
session = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
def preprocess(image_path):
image = np.asarray(Image.open(image_path).convert("RGB"), dtype=np.float32) / 255.0
height, width = image.shape[:2]
scale = min(768 / width, 768 / height)
new_width, new_height = int(width * scale), int(height * scale)
resized = Image.fromarray((image * 255).astype(np.uint8)).resize((new_width, new_height))
canvas = np.zeros((768, 768, 3), dtype=np.float32)
top = (768 - new_height) // 2
left = (768 - new_width) // 2
canvas[top:top + new_height, left:left + new_width] = np.asarray(resized) / 255.0
return np.transpose(canvas, (2, 0, 1))[None].astype(np.float32), (scale, top, left)
input_tensor, transform = preprocess("ruler.jpg")
init_point, dist, ratio, direction, points_info = session.run(
None, {"input": input_tensor}
)
print("initial point:", init_point[0])
print("base distance:", dist[0])
print("geometric-progression ratio:", ratio[0])
print("ruler direction:", direction[0])
print("point count and bounds:", points_info[0])
```
The five outputs are:
| Output | Meaning |
| --- | --- |
| `init_point` | Predicted starting ruler-mark location in the 768 × 768 processed image |
| `dist` | Base distance between generated marks |
| `ratio` | Geometric-progression ratio between consecutive mark spacings |
| `direction` | Unit direction vector along the ruler |
| `points_info` | Number of generated points followed by `[min_x, min_y, max_x, max_y]` valid bounds |
To reconstruct the full set of ruler-mark positions and calculate the median
pixels-per-centimeter value, use the post-processing in the
[demo implementation](https://huggingface.co/spaces/ymp5078/RulerNet-Demo/blob/main/app.py).
The resulting coordinates are in the padded 768 × 768 image. To map a point
`(x, y)` back to the original image, use `(x - left) / scale` and
`(y - top) / scale`, where `scale`, `top`, and `left` are returned by
`preprocess`.
## License and commercial use
This material is licensed under [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/).
For commercial-use licensing inquiries, contact [jwang@ist.psu.edu](mailto:jwang@ist.psu.edu). |