Model Sources
Retto supports multiple ways to provide OCR models:
Local Files Load models from the filesystem
HuggingFace Hub Automatically download from HF Hub
Embedded Blobs Compile models into your binary
Model Types
Retto requires three models for full OCR functionality:
Text Detection Model - Locates text regions in imagesDefault: ch_PP-OCRv4_det_infer.onnx
Text Classification Model - Determines text orientationDefault: ch_ppocr_mobile_v2.0_cls_infer.onnx
Text Recognition Model - Recognizes characters in text regionsDefault: ch_PP-OCRv4_rec_infer.onnx
Additionally, the recognition model requires a character dictionary file:
Character Dictionary - Maps model outputs to charactersDefault: ppocr_keys_v1.txt
Loading from Local Files
The simplest approach for desktop applications:
use retto_core :: prelude ::* ;
let models = RettoOrtWorkerModelProvider ( RettoWorkerModelProvider {
det : RettoWorkerModelSource :: Path ( "models/ch_PP-OCRv4_det_infer.onnx" . into ()),
cls : RettoWorkerModelSource :: Path ( "models/ch_ppocr_mobile_v2.0_cls_infer.onnx" . into ()),
rec : RettoWorkerModelSource :: Path ( "models/ch_PP-OCRv4_rec_infer.onnx" . into ()),
});
let cfg = RettoSessionConfig {
worker_config : RettoOrtWorkerConfig {
device : RettoOrtWorkerDevice :: CPU ,
models ,
},
rec_processor_config : RecProcessorConfig {
character_source : RecCharacterDictProvider :: OutSide (
RettoWorkerModelSource :: Path ( "models/ppocr_keys_v1.txt" . into ())
),
.. Default :: default ()
},
.. Default :: default ()
};
If the specified files don’t exist, session creation will fail with a ModelNotFoundError.
Using Default Paths
For convenience, use the built-in defaults:
let models = RettoOrtWorkerModelProvider :: from_local_v4_path_default ();
This expects models in the current directory with standard names:
ch_PP-OCRv4_det_infer.onnx
ch_PP-OCRv4_rec_infer.onnx
ch_ppocr_mobile_v2.0_cls_infer.onnx
Loading from HuggingFace Hub
Enable the hf-hub feature to automatically download models:
[ dependencies ]
retto-core = { version = "0.1.5" , features = [ "hf-hub" , "backend-ort" ] }
Then use the HuggingFace provider:
let models = RettoOrtWorkerModelProvider :: from_hf_hub_v4_default ();
let cfg = RettoSessionConfig {
worker_config : RettoOrtWorkerConfig {
device : RettoOrtWorkerDevice :: CPU ,
models ,
},
.. Default :: default ()
};
Models are downloaded automatically
On first run, models are fetched from pk5ls20/PaddleModel on HuggingFace Hub.
Models are cached locally
Subsequent runs reuse the cached models from ~/.cache/huggingface/.
Progress is shown
Download progress is displayed via tracing logs.
Custom HuggingFace Repository
Specify a custom repository:
let models = RettoOrtWorkerModelProvider ( RettoWorkerModelProvider {
det : RettoWorkerModelSource :: HuggingFace {
repo : "your-org/your-models" . to_string (),
model : "path/to/det.onnx" . to_string (),
},
rec : RettoWorkerModelSource :: HuggingFace {
repo : "your-org/your-models" . to_string (),
model : "path/to/rec.onnx" . to_string (),
},
cls : RettoWorkerModelSource :: HuggingFace {
repo : "your-org/your-models" . to_string (),
model : "path/to/cls.onnx" . to_string (),
},
});
Embedding Models
For WASM or self-contained binaries, embed models at compile time.
Enable Feature Flag
[ dependencies ]
retto-core = { version = "0.1.5" , features = [ "download-models" , "backend-ort-wasm" ] }
Build-Time Download
From build.rs, models are downloaded during compilation:
// Models are downloaded to ./models/ during build
// and embedded using include_bytes!
Use Embedded Models
let models = RettoOrtWorkerModelProvider :: from_local_v4_blob_default ();
let cfg = RettoSessionConfig {
worker_config : RettoOrtWorkerConfig {
device : RettoOrtWorkerDevice :: CPU ,
models ,
},
.. Default :: default ()
};
From ort_worker.rs:87, the implementation uses include_bytes!:
fn from_local_v4_blob_default () -> Self {
Self ( RettoWorkerModelProvider {
det : RettoWorkerModelSource :: Blob (
include_bytes! ( "../../models/ch_PP-OCRv4_det_infer.onnx" ) . to_vec (),
),
rec : RettoWorkerModelSource :: Blob (
include_bytes! ( "../../models/ch_PP-OCRv4_rec_infer.onnx" ) . to_vec (),
),
cls : RettoWorkerModelSource :: Blob (
include_bytes! ( "../../models/ch_ppocr_mobile_v2.0_cls_infer.onnx" ) . to_vec (),
),
})
}
Embedding models significantly increases binary size (typically ~30-50 MB).
WASM-Specific Loading
For WebAssembly, models can be loaded from JavaScript:
import { Retto } from '@nekoimageland/retto-wasm' ;
const retto = await Retto . load ();
// Option 1: Use embedded models (if built with embed-models feature)
await retto . init ();
// Option 2: Load external models
const models = {
det_model: await fetch ( '/models/det.onnx' ). then ( r => r . arrayBuffer ()),
cls_model: await fetch ( '/models/cls.onnx' ). then ( r => r . arrayBuffer ()),
rec_model: await fetch ( '/models/rec.onnx' ). then ( r => r . arrayBuffer ()),
rec_dict: await fetch ( '/models/dict.txt' ). then ( r => r . arrayBuffer ()),
};
await retto . init ( models );
From wasm_lib.rs:80, the WASM interface accepts raw buffers:
pub unsafe extern "C" fn retto_init (
det_ptr : * const u8 ,
det_len : usize ,
cls_ptr : * const u8 ,
cls_len : usize ,
rec_ptr : * const u8 ,
rec_len : usize ,
rec_dict_ptr : * const u8 ,
rec_dict_len : usize ,
) {
let det_model = unsafe { std :: slice :: from_raw_parts ( det_ptr , det_len ) . to_vec () };
let cls_model = unsafe { std :: slice :: from_raw_parts ( cls_ptr , cls_len ) . to_vec () };
let rec_model = unsafe { std :: slice :: from_raw_parts ( rec_ptr , rec_len ) . to_vec () };
let rec_dict = unsafe { std :: slice :: from_raw_parts ( rec_dict_ptr , rec_dict_len ) . to_vec () };
// Initialize session with blob sources...
}
Model Resolution
From worker.rs:30, the resolution logic:
impl RettoWorkerModelSource {
pub ( crate ) fn resolve ( self ) -> RettoResult < RettoWorkerModelResolvedSource > {
match self {
RettoWorkerModelSource :: Path ( path ) => {
let path = std :: path :: PathBuf :: from ( path );
match path . exists () {
true => Ok ( RettoWorkerModelResolvedSource :: Path ( path )),
false => Err ( RettoError :: ModelNotFoundError (
path . into_os_string () . to_string_lossy () . to_string (),
)),
}
}
RettoWorkerModelSource :: Blob ( blob ) => match blob . is_empty () {
true => Err ( RettoError :: ModelNotFoundError (
"Empty model blob!" . to_string (),
)),
false => Ok ( RettoWorkerModelResolvedSource :: Blob ( blob )),
},
RettoWorkerModelSource :: HuggingFace { repo , model } => {
use crate :: hf_hub_helper :: HfHubHelper ;
let helper = HfHubHelper :: new ();
let path = helper . get_model_file ( & repo , & model ) ? ;
Ok ( RettoWorkerModelResolvedSource :: Path ( path ))
}
}
}
}
Comparison
Local Files
HuggingFace Hub
Embedded
Pros:
Fast loading
No network required
Full control over model versions
Cons:
Must distribute models separately
User must download models
Path configuration required
Best for: Desktop applications, serversPros:
Automatic downloads
Easy updates
Centralized model storage
Cons:
Requires network on first run
Depends on HuggingFace availability
Adds hf-hub dependency
Best for: Development, prototypes, auto-updating appsPros:
No external dependencies
Works offline
Single binary distribution
Cons:
Large binary size (+30-50 MB)
Longer compile times
Model updates require recompilation
Best for: WebAssembly, embedded systems, portable apps
Default Behavior
From worker.rs:81, the default provider selection:
fn default_provider () -> Self {
#[cfg(all(not(target_family = "wasm" ), feature = "hf-hub" ))]
return Self :: from_hf_hub_v4_default ();
#[cfg(all(not(target_family = "wasm" ), not(feature = "hf-hub" )))]
return Self :: from_local_v4_path_default ();
#[cfg(target_family = "wasm" )]
return Self :: from_local_v4_blob_default ();
}
Using RettoOrtWorkerConfig::default() or RettoOrtWorkerModelProvider::default() will automatically select the appropriate provider based on your build configuration.
Next Steps
Backends Configure execution backends
Rust Usage Learn the core Rust API