Fast OCR library for Foxhole stockpile screenshots
Project description
fs-ocr
Fast OCR library for Foxhole stockpile screenshots, written in Rust with Python bindings.
Features
- Fast Template Matching: pHash filtering + NCC scoring with adaptive candidate escalation
- ROI + Grey Mask Detection: black-box ROI localization followed by grey-mask box detection
- Quantity Recognition: template-based glyph matching (no OCR engine needed for digits)
- Pure-Rust OCR:
ocrs/rtenwith an embedded model — reads Latin + Russian and localized stockpile types; Chinese custom names via the optional systemtesseractCLI (see Language support) - Python API + CLI: PyO3 bindings (
import fs_ocr) and anfs-ocrcommand-line tool
Installation
Standalone CLI (no Python required)
Prebuilt fs-ocr binaries are attached to each GitHub Release
for Linux, Windows, and macOS (x86_64 + Apple Silicon). Download, extract, run:
tar -xzf fs-ocr-linux-x86_64.tar.gz # or unzip the .zip on Windows
./fs-ocr scan screenshot.png -d templates.h5 --faction wardens
One static build is published per platform (fs-ocr-<os>-<arch>) — pure-Rust
OCR, no system dependencies. (The system tesseract CLI is used at runtime
only if present, for Chinese custom names.)
From PyPI
pip install fs-ocr
OCR is pure Rust (ocrs/rten) with the recognition model embedded in the
wheel — no system OCR libraries are required to install or run.
You still need a template database (
.h5) to scan against; it is not bundled in the wheel. See Template Database.
Language support
The embedded ocrs recognizer reads Latin and Russian (Cyrillic) text
natively, and the closed set of localized stockpile-type names (including
Chinese). No extra packages or language flags are needed for any of that.
The one exception is free-form Chinese custom names, which are read via the
system tesseract CLI if it is installed — detected at runtime, entirely
optional. If tesseract is absent, everything else still works and only the
Chinese custom name is left unread (no error).
| OS | Optional install (Chinese custom names) |
|---|---|
| Debian/Ubuntu | sudo apt install tesseract-ocr tesseract-ocr-chi-sim |
| Fedora/RHEL | sudo dnf install tesseract tesseract-langpack-chi_sim |
| macOS (Homebrew) | brew install tesseract tesseract-lang |
| Windows | UB Mannheim installer (select Chinese) or choco install tesseract |
Override the binary or language via FS_OCR_TESSERACT / FS_OCR_TESSERACT_LANG
(defaults tesseract / chi_sim).
From Source
The build needs only a C/C++ toolchain — HDF5 is built from source via the
static-hdf5 feature. No OpenCV or Tesseract dev libraries required:
# Build deps (Ubuntu/Debian)
sudo apt-get install cmake gcc g++ libclang-dev
# Build and install the Python module
pip install maturin
maturin develop --release
# Or build the standalone CLI (no Python / libpython linkage)
cargo build --release --no-default-features --bin fs-ocr
Python Usage
from fs_ocr import StockpileScanner, ScanConfig
import numpy as np
# Create scanner (data_path holds the OCR model files, default "data")
scanner = StockpileScanner(database_path="templates.h5", data_path="data")
# Scan from NumPy array (H x W x 3, uint8, BGR)
image = np.array(...) # Your image data
result = scanner.scan(image, faction="wardens")
print(result.to_json())
# Scan from file
result = scanner.scan_file("screenshot.png", faction="colonials")
# With custom config
config = ScanConfig(confidence_gap=0.02)
result = scanner.scan(image, config=config)
# Access result data
for item in result.items:
print(f"{item.code}: {item.quantity} (confidence: {item.confidence:.2f})")
API Reference
StockpileScanner
Main scanner class.
scanner = StockpileScanner(
database_path: str, # Path to HDF5 template database
data_path: str = "data" # Path to OCR model files directory
)
result = scanner.scan(
image: np.ndarray, # BGR image (H x W x 3, uint8)
faction: str = None, # "wardens", "colonials", or None
config: ScanConfig = None
)
result = scanner.scan_file(
image_path: str, # Path to image file
faction: str = None,
config: ScanConfig = None
)
scan_debug(image, ...) and scan_debug_file(path, ...) take the same
arguments and return the same Stockpile, but additionally populate each
item's debug_candidates with the broad diagnostic candidate set (see
StockpileItem). The normal scan/scan_file output is
unchanged. Intended for tooling that needs to inspect why an icon matched.
ScanConfig
Configuration options for tuning the matching pipeline.
config = ScanConfig(
phash_threshold: int = 15, # Max Hamming distance for pHash filter (lower=faster)
max_ncc_candidates: int = 100, # Hard cap on NCC candidates (upper bound of escalation)
ncc_initial_candidates: int = 25, # Initial NCC batch before adaptive escalation
ncc_escalation_threshold: float = 0.9,# Escalate candidate count if best confidence below this
confidence_gap: float = 0.0, # Return alternatives within this gap of best match
ncc_tiebreaker_threshold: float = 0.003 # Edge(Sobel)-based tiebreaker; 0.0 disables
)
# Serialize/deserialize
config.to_json() -> str
ScanConfig.from_json(json_str) -> ScanConfig
Stockpile
Scan result containing detected items.
stockpile.name # Custom stockpile name (if applicable)
stockpile.type # StockpileType enum
stockpile.is_reserve # True when named something other than "Public"
stockpile.items # List[StockpileItem]
stockpile.timestamp # ISO 8601 scan timestamp
stockpile.shard # Game shard name
stockpile.ingame_timestamp # In-game time (e.g. "Day 1293, 1906 Hours")
stockpile.resolution # Screenshot resolution ("WxH")
stockpile.errors # List of error messages
stockpile.timing # Optional[Timing] per-stage metrics (None unless collected)
stockpile.to_json() # Serialize to JSON (to_json_compact() for one line)
StockpileItem
Individual detected item.
item.code # Item code or "Unknown"
item.quantity # Detected quantity (-1 if failed)
item.crated # Whether item is crated
item.confidence # Match confidence (0.0 - 1.0)
item.x, item.y # Icon top-left (px) in the source screenshot
item.candidates # Alternative matches (if confidence_gap > 0)
item.debug_candidates # Broad diagnostic set; None unless scanned via scan_debug
DebugCandidate
Populated only by scan_debug / scan_debug_file. One entry per template that
passed the icon's pHash threshold for its crated state — any
code/category/mod/faction — NCC-scored and ranked descending, capped by
max_ncc_candidates. The item's code/confidence equals the top candidate.
candidate.code # Item code
candidate.confidence # NCC score (0.0 - 1.0)
candidate.mod # Mod name (e.g. "vanilla", "airborne")
candidate.category # "item" / "vehicle" / "shippable" / "invalid"
candidate.crated # Whether the template is crated
candidate.faction # "neutral" / "Colonials" / "Wardens"
candidate.phash_distance # Hamming distance icon-to-template (lower = closer)
CLI Usage
The crate also builds an fs-ocr binary that emits JSON.
# Scan a file
fs-ocr scan screenshot.png -d templates.h5 --faction wardens
# Read image from stdin ("-" or omit the path)
cat screenshot.png | fs-ocr scan -d templates.h5 --compact
# Print version
fs-ocr version
Matching can be tuned with --phash-threshold, --max-ncc-candidates,
--ncc-initial-candidates, --ncc-escalation-threshold, --ncc-tiebreaker,
and --confidence-gap. Set FS_OCR_TIMING=1 to include per-stage timing in the
output. Exit codes: 0 ok, 1 runtime error, 2 bad input.
Template Database
fs-ocr matches icons against a pre-built HDF5 template database, supplied at
runtime via the CLI --database flag or the StockpileScanner(database_path=...)
argument. It is not bundled with the wheel or the CLI binary.
The database is generated separately from game assets by the
foxhole-stockpiles project. Grab
the .h5 file from its data/
directory and pass its path via --database / database_path.
fs-ocr scan screenshot.png -d fs_airborne.h5
Development
Commands
| Command | Description |
|---|---|
cargo test |
Run the Rust test suite |
cargo clippy --all-targets -- -D warnings |
Run linter (warnings as errors) |
cargo fmt |
Format code with rustfmt |
cargo build --release |
Build optimized library |
cargo build --release --no-default-features --bin fs-ocr |
Build the standalone CLI (no libpython) |
maturin develop --release |
Build and install Python module (dev) |
maturin build --release |
Build Python wheel for distribution |
Feature Flags
| Feature | Default | Description |
|---|---|---|
python |
on | PyO3 + numpy bindings; drop with --no-default-features for a pure CLI |
static-hdf5 |
off | Build/statically link libhdf5 from source (used by CI wheels) |
Requirements
- Rust toolchain (edition 2021)
- Python 3.10+ (for bindings)
- Build tools:
cmake,gcc/g++,libclang-dev - Optional runtime: system
tesseractCLI (Chinese custom names only)
Dev Dependencies
pip install pytest numpy # Python dev deps
Architecture
src/
├── lib.rs # PyO3 module + StockpileScanner
├── bin/fs-ocr.rs # CLI binary (clap)
├── constants.rs # Hardcoded values / resolution scaling
├── error.rs # Error types
├── config.rs # ScanConfig
├── image_utils.rs # RGB→grayscale, crop helpers
├── models/ # Output structs
│ ├── stockpile.rs
│ ├── stockpile_item.rs
│ └── timing.rs # Per-stage Timing
├── enums/ # Type enums
│ ├── stockpile_type.rs
│ ├── item_faction.rs
│ └── item_category.rs
├── detector/ # ROI + grey mask detection
│ ├── black_box.rs # Dark ROI localization (first pass)
│ ├── geometry.rs
│ └── grey_mask/ # detector + morphology + grouping
├── template/ # Template matching
│ ├── database.rs # HDF5 loading
│ ├── matching.rs # NCC + adaptive escalation + tiebreaker
│ ├── phash.rs # Perceptual hashing
│ └── {label,public,type}_match.rs # embedded-asset template matchers
├── ocr/ # Text + quantity extraction
│ ├── engine.rs # OcrEngine trait + OcrConfig
│ ├── basic.rs # OcrsEngine (pure-Rust ocrs)
│ ├── digit_matcher.rs# Glyph-template digit recognition (quantities)
│ ├── preprocess.rs # upscale helpers
│ ├── quantity.rs # Quantity validation
│ └── tesseract.rs # ChineseNameReader (system `tesseract` CLI)
└── coordinator/ # Pipeline orchestration
├── pipeline.rs
├── region_preprocess.rs # OCR image prep (luma/contrast/framing/upscale)
├── metadata_parse.rs # shard / timestamp / public-name parsing
└── validation.rs
License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fs_ocr-1.1.0.tar.gz.
File metadata
- Download URL: fs_ocr-1.1.0.tar.gz
- Upload date:
- Size: 9.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3b7dab0bcc6c7626c5aca5ddc337311fb5b23aa4987d2f57ec1324df70920d3
|
|
| MD5 |
48349fc59feedd848bef898c6b15f13c
|
|
| BLAKE2b-256 |
c3371095aacad0d41064586b7727e37162d9979d0fbbb59768fdbe4539c5243f
|
Provenance
The following attestation bundles were made for fs_ocr-1.1.0.tar.gz:
Publisher:
release.yml on xurxogr/fs-ocr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fs_ocr-1.1.0.tar.gz -
Subject digest:
c3b7dab0bcc6c7626c5aca5ddc337311fb5b23aa4987d2f57ec1324df70920d3 - Sigstore transparency entry: 1918937849
- Sigstore integration time:
-
Permalink:
xurxogr/fs-ocr@e4b1b475f77ea41ec6482353a09c52f690258999 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/xurxogr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e4b1b475f77ea41ec6482353a09c52f690258999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fs_ocr-1.1.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: fs_ocr-1.1.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 14.6 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
600c1e45837a2b7117f43fb5d044596dc60799264c8daca543007c3d4ac48831
|
|
| MD5 |
e08859e44d74756c615c509813803869
|
|
| BLAKE2b-256 |
ed857b40a2c2905584aadcb0e5cb88c74bc6d405334e59e1d55f2393d82e238f
|
Provenance
The following attestation bundles were made for fs_ocr-1.1.0-cp310-abi3-win_amd64.whl:
Publisher:
release.yml on xurxogr/fs-ocr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fs_ocr-1.1.0-cp310-abi3-win_amd64.whl -
Subject digest:
600c1e45837a2b7117f43fb5d044596dc60799264c8daca543007c3d4ac48831 - Sigstore transparency entry: 1918938092
- Sigstore integration time:
-
Permalink:
xurxogr/fs-ocr@e4b1b475f77ea41ec6482353a09c52f690258999 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/xurxogr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e4b1b475f77ea41ec6482353a09c52f690258999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fs_ocr-1.1.0-cp310-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: fs_ocr-1.1.0-cp310-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 13.7 MB
- Tags: CPython 3.10+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5d77a366b346f61df451d2058f1d538cf9d1f501a9f46024d68647ffc16ed62b
|
|
| MD5 |
52fd22b08849581827f4a487e5d54dfc
|
|
| BLAKE2b-256 |
53327e793edc7b0436a7ef665e2c3594a63ff6218a350b84595cafdd7987f624
|
Provenance
The following attestation bundles were made for fs_ocr-1.1.0-cp310-abi3-manylinux_2_28_x86_64.whl:
Publisher:
release.yml on xurxogr/fs-ocr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fs_ocr-1.1.0-cp310-abi3-manylinux_2_28_x86_64.whl -
Subject digest:
5d77a366b346f61df451d2058f1d538cf9d1f501a9f46024d68647ffc16ed62b - Sigstore transparency entry: 1918938324
- Sigstore integration time:
-
Permalink:
xurxogr/fs-ocr@e4b1b475f77ea41ec6482353a09c52f690258999 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/xurxogr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e4b1b475f77ea41ec6482353a09c52f690258999 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fs_ocr-1.1.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: fs_ocr-1.1.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 12.9 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9650d5e773967ee945abf9edcce498f260af4624a5f9ba167adec1e50b49a538
|
|
| MD5 |
82366824747e83e6a62376e251553d1a
|
|
| BLAKE2b-256 |
2876028551b51b0fe560b2e55a22f856fe1dd4f63ef6db1e45357328321350c1
|
Provenance
The following attestation bundles were made for fs_ocr-1.1.0-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on xurxogr/fs-ocr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fs_ocr-1.1.0-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
9650d5e773967ee945abf9edcce498f260af4624a5f9ba167adec1e50b49a538 - Sigstore transparency entry: 1918937968
- Sigstore integration time:
-
Permalink:
xurxogr/fs-ocr@e4b1b475f77ea41ec6482353a09c52f690258999 -
Branch / Tag:
refs/tags/v1.1.0 - Owner: https://github.com/xurxogr
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e4b1b475f77ea41ec6482353a09c52f690258999 -
Trigger Event:
push
-
Statement type: