Overview
Therun_stream() method allows you to receive real-time updates as each stage of the OCR pipeline completes. This is useful for:
- Showing progress to users in long-running operations
- Processing results incrementally
- Building responsive UI applications
- Debugging and monitoring the pipeline
OCR Pipeline Stages
Retto’s OCR pipeline consists of three stages:- Detection (Det): Locates text regions in the image
- Classification (Cls): Determines text orientation (0° or 180°)
- Recognition (Rec): Recognizes actual text content
run_stream(), you receive results as each stage completes instead of waiting for the entire pipeline.
Basic Streaming Example
use retto_core::prelude::*;
use std::sync::mpsc;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create configuration
let cfg = RettoSessionConfig {
worker_config: RettoOrtWorkerConfig::default(),
..Default::default()
};
let mut session = RettoSession::new(cfg)?;
// Create a channel for receiving stage results
let (sender, receiver) = mpsc::channel();
// Load image
let image_bytes = fs::read("input.png")?;
// Start OCR in a separate thread
let handle = std::thread::spawn(move || {
session.run_stream(image_bytes, sender)
});
// Process results as they arrive
while let Ok(stage_result) = receiver.recv() {
match stage_result {
RettoWorkerStageResult::Det(det_result) => {
println!("✓ Detection complete: {} regions found",
det_result.0.len());
for (idx, det) in det_result.0.iter().enumerate() {
println!(" Region {}: score={:.2}", idx, det.score);
}
}
RettoWorkerStageResult::Cls(cls_result) => {
println!("✓ Classification complete");
for (idx, cls) in cls_result.0.iter().enumerate() {
println!(" Region {}: angle={}°, confidence={:.2}",
idx, cls.label.label, cls.label.score);
}
}
RettoWorkerStageResult::Rec(rec_result) => {
println!("✓ Recognition complete");
for (idx, rec) in rec_result.0.iter().enumerate() {
println!(" Region {}: '{}' (confidence={:.2})",
idx, rec.text, rec.score);
}
// Recognition is the final stage
break;
}
}
}
// Wait for processing to complete
handle.join().unwrap()?;
println!("\n✓ All stages complete!");
Ok(())
}
Understanding Stage Results
Each stage produces a specific result type:Detection Stage
RettoWorkerStageResult::Det(det_result) => {
// det_result: DetProcessorResult
// Contains Vec<DetProcessorInnerResult>
for det in det_result.0.iter() {
// det.boxes: PointBox<OrderedFloat<f32>>
// Four corner points of the detected text region
let tl = det.boxes.tl();
let tr = det.boxes.tr();
let br = det.boxes.br();
let bl = det.boxes.bl();
// det.score: f32
// Detection confidence (0.0 to 1.0)
let confidence = det.score;
}
}
Classification Stage
RettoWorkerStageResult::Cls(cls_result) => {
// cls_result: ClsProcessorResult
// Contains Vec<ClsProcessorSingleResult>
for cls in cls_result.0.iter() {
// cls.label.label: u16
// Rotation angle: 0 or 180 degrees
let angle = cls.label.label;
// cls.label.score: f32
// Classification confidence (0.0 to 1.0)
let confidence = cls.label.score;
}
}
Recognition Stage
RettoWorkerStageResult::Rec(rec_result) => {
// rec_result: RecProcessorResult
// Contains Vec<RecProcessorSingleResult>
for rec in rec_result.0.iter() {
// rec.text: String
// Recognized text content
let text = &rec.text;
// rec.score: f32
// Recognition confidence (0.0 to 1.0)
let confidence = rec.score;
}
}
Progress Monitoring Example
Here’s a more advanced example that shows progress with timing information:use retto_core::prelude::*;
use std::sync::mpsc;
use std::time::Instant;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = RettoSessionConfig {
worker_config: RettoOrtWorkerConfig::default(),
..Default::default()
};
let mut session = RettoSession::new(cfg)?;
let (sender, receiver) = mpsc::channel();
let image_bytes = fs::read("document.png")?;
let start_time = Instant::now();
let mut stage_times = Vec::new();
let mut last_time = start_time;
// Spawn processing thread
let handle = std::thread::spawn(move || {
session.run_stream(image_bytes, sender)
});
println!("Starting OCR pipeline...\n");
// Collect results from all stages
let mut det_result = None;
let mut cls_result = None;
let mut rec_result = None;
while let Ok(stage_result) = receiver.recv() {
let elapsed = last_time.elapsed();
stage_times.push(elapsed);
last_time = Instant::now();
match stage_result {
RettoWorkerStageResult::Det(result) => {
println!("[1/3] Detection: {} regions in {:?}",
result.0.len(), elapsed);
det_result = Some(result);
}
RettoWorkerStageResult::Cls(result) => {
println!("[2/3] Classification: {} regions in {:?}",
result.0.len(), elapsed);
cls_result = Some(result);
}
RettoWorkerStageResult::Rec(result) => {
println!("[3/3] Recognition: {} regions in {:?}",
result.0.len(), elapsed);
rec_result = Some(result);
break;
}
}
}
handle.join().unwrap()?;
// Print summary
let total_time = start_time.elapsed();
println!("\n" + "=".repeat(50));
println!("Total time: {:?}", total_time);
// Print final results
if let (Some(det), Some(cls), Some(rec)) = (det_result, cls_result, rec_result) {
println!("\nRecognized Text:");
println!("=".repeat(50));
for (idx, rec) in rec.0.iter().enumerate() {
println!("{}. {} (confidence: {:.1}%)",
idx + 1, rec.text, rec.score * 100.0);
}
}
Ok(())
}
Building a Progress Bar
Integrate with a progress bar library likeindicatif:
use retto_core::prelude::*;
use std::sync::mpsc;
use std::fs;
use indicatif::{ProgressBar, ProgressStyle};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = RettoSessionConfig {
worker_config: RettoOrtWorkerConfig::default(),
..Default::default()
};
let mut session = RettoSession::new(cfg)?;
let (sender, receiver) = mpsc::channel();
let image_bytes = fs::read("input.png")?;
// Create progress bar
let pb = ProgressBar::new(3);
pb.set_style(
ProgressStyle::default_bar()
.template("{msg} [{bar:40}] {pos}/{len}")
.unwrap()
.progress_chars("#>-")
);
// Spawn processing
let handle = std::thread::spawn(move || {
session.run_stream(image_bytes, sender)
});
// Update progress as stages complete
while let Ok(stage_result) = receiver.recv() {
match stage_result {
RettoWorkerStageResult::Det(_) => {
pb.set_message("Detecting text regions...");
pb.inc(1);
}
RettoWorkerStageResult::Cls(_) => {
pb.set_message("Classifying orientations...");
pb.inc(1);
}
RettoWorkerStageResult::Rec(_) => {
pb.set_message("Recognizing text...");
pb.inc(1);
break;
}
}
}
handle.join().unwrap()?;
pb.finish_with_message("OCR complete!");
Ok(())
}
Comparison with run()
Here’s howrun_stream() differs from the standard run() method:
let result = session.run(image_bytes)?;
// Wait for all stages to complete
// Get all results at once
println!("Found: {} regions", result.det_result.0.len());
println!("Text: {:?}", result.rec_result.0);
Error Handling
Handle errors in the streaming pipeline:use retto_core::prelude::*;
use std::sync::mpsc;
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg = RettoSessionConfig {
worker_config: RettoOrtWorkerConfig::default(),
..Default::default()
};
let mut session = RettoSession::new(cfg)?;
let (sender, receiver) = mpsc::channel();
let image_bytes = fs::read("input.png")?;
let handle = std::thread::spawn(move || {
session.run_stream(image_bytes, sender)
});
// Process results with error handling
let mut stage_count = 0;
while let Ok(stage_result) = receiver.recv() {
stage_count += 1;
match stage_result {
RettoWorkerStageResult::Det(result) => {
println!("Stage 1/3 complete");
}
RettoWorkerStageResult::Cls(result) => {
println!("Stage 2/3 complete");
}
RettoWorkerStageResult::Rec(result) => {
println!("Stage 3/3 complete");
break;
}
}
}
// Check if pipeline completed successfully
match handle.join() {
Ok(Ok(())) => {
if stage_count == 3 {
println!("✓ Pipeline completed successfully");
} else {
eprintln!("⚠ Pipeline incomplete: only {} stages", stage_count);
}
}
Ok(Err(e)) => eprintln!("✗ Pipeline error: {:?}", e),
Err(e) => eprintln!("✗ Thread panic: {:?}", e),
}
Ok(())
}
Next Steps
Basic OCR
Learn the fundamentals of OCR with Retto
Custom Configuration
Fine-tune processing parameters for your use case
