How To

Menggunakan Rust

Penggunaan mesin dan API RustO! berbasis Rust murni pada aplikasi Rust.

Crate rusto-rs adalah mesin inti Rust murni yang mentenagai seluruh binding platform RustO!. Crate ini menyediakan abstraksi tanpa biaya (zero-cost), jaminan keamanan memori, serta pra-pemrosesan gambar terakselerasi SIMD.

Instalasi

Tambahkan rusto-rs ke dalam Cargo.toml:

[dependencies]
rusto-rs = "0.3.2"

1. Inisialisasi Mesin (InitializeConfig)

Inisialisasi mesin OCR satu kali per siklus aplikasi atau thread worker. RustO! menyediakan konfigurasi preset yang sesuai dengan generasi model PaddleOCR:

use rusto::{InitializeConfig, RustO};

// Inisialisasi dengan preset standar PP-OCRv6
let config = InitializeConfig::ppv6("models/det.rten", "models/rec.rten", "models/dict.txt")
    .with_det_thresh(0.3)
    .with_det_box_thresh(0.6)
    .with_limit_side_len(736)
    .with_limit_type("min")
    .with_unclip_ratio(2.0)
    .with_use_dilation(true)
    .with_text_score(0.5)
    .with_rec_batch_num(6);

let mut ocr = RustO::initialize(config)?;

Model Tambahan (Klasifikasi Orientasi & CLS)

Untuk dokumen hasil pemindaian yang miring atau teks terbalik, sertakan model klasifikasi orientasi:

// PP-OCRv5 dengan pengklasifikasi baris terbalik 180° (CLS) dan rotasi halaman penuh
let config = InitializeConfig::ppv5("models/det.rten", "models/rec.rten", "models/dict.txt")
    .with_cls_threshold("models/cls.rten", 0.9)
    .with_orientation_threshold("models/orient.rten", 0.85);

let mut ocr = RustO::initialize(config)?;

Komposisi Mesin Generik & Pluggable

RustO! menyediakan trait InferenceSession dan enum EngineSession sehingga Anda dapat mengganti mesin inferensi dengan mudah (misalnya ONNX Runtime ort, Tract, Candle, atau mesin pengujian tiruan) tanpa menyentuh logika geometri OCR:

use rusto::{InferenceSession, EngineError, RustO, InitializeConfig, TextDetector, TextRecognizer};
use ndarray::ArrayD;
use std::collections::HashMap;

// Pilihan 1: Berikan sesi inferensi kustom
let config = InitializeConfig::ppv6("models/det.rten", "models/rec.rten", "models/dict.txt");
let ocr = RustO::with_custom_engines(config, my_det_session, my_rec_session)?;

// Pilihan 2: Gabungkan TextDetector dan TextRecognizer yang telah dikonfigurasi sebelumnya
let det = TextDetector::new(config.det.clone())?;
let rec = TextRecognizer::new(config.rec.clone())?;
let ocr = RustO::with_components(config.global, det, rec)?;

2. Opsi Permintaan Lokal (OcrRunOptions)

Batas ukuran gambar, penskalaan detektor, dilatasi morfologi, rasio unclip, dan mode keluaran dapat dikonfigurasi per panggilan deteksi melalui OcrRunOptions tanpa mengubah sesi model yang telah diinisialisasi:

use rusto::{
    DetectionRunOptions, ImageSource, OcrRunOptions, OutputGranularity,
    PostprocessRunOptions,
};

let options = OcrRunOptions {
    output: OutputGranularity::Lines, // OutputGranularity::Words atau OutputGranularity::Spatial
    line_y_threshold: Some(0.5),      // Toleransi dinamis pengelompokan baris
    word_x_threshold: Some(0.4),      // Toleransi pemisahan kata/kolom
    text_score: Some(0.55),           // Batas skor kepercayaan pengenalan teks
    classification: Some(true),       // Evaluasi CLS jika model tersedia
    max_side_len: Some(1600.0),       // Batas panjang sisi maksimum gambar
    min_height: Some(32.0),           // Tinggi minimum kotak teks
    detection: Some(DetectionRunOptions {
        limit_side_len: Some(960),
        limit_type: Some("max".into()),
        ..Default::default()
    }),
    postprocess: Some(PostprocessRunOptions {
        use_dilation: Some(true),     // Dilatasi morfologi untuk struk redup/pudar
        unclip_ratio: Some(2.0),
        threshold: Some(0.3),
        box_threshold: Some(0.6),
        ..Default::default()
    }),
    ..Default::default()
};

3. Sumber Gambar yang Didukung

ImageSource menerima path berkas sistem maupun buffer byte dalam memori:

use rusto::ImageSource;
use std::path::PathBuf;

// 1. Path berkas filesystem (memuat & mendekode secara efisien)
let source_path = ImageSource::Path(PathBuf::from("data/invoice.jpg"));

// 2. Buffer byte mentah dalam memori RAM (PNG, JPEG, WebP, BMP)
let raw_bytes: Vec<u8> = std::fs::read("data/invoice.jpg")?;
let source_bytes = ImageSource::Bytes(raw_bytes);

4. Contoh Praktis & Penanganan Keluaran

Contoh A: Mengekstrak Baris Teks Terstruktur

use rusto::{DetectTextResult, ImageSource, OcrRunOptions, OutputGranularity, RustO};

let mut options = OcrRunOptions::default();
options.output = OutputGranularity::Lines;

let result = ocr.detect_text(&ImageSource::Path("invoice.png".into()), &options)?;

if let DetectTextResult::Structured(items) = result {
    for item in items {
        println!("Teks: {}", item.text);
        println!("Skor: {:.3}", item.score);
        println!("Titik Poligon [TL, TR, BR, BL]: {:?}", item.box_points);
        println!(
            "Frame Kotak Batas: Kiri={}, Atas={}, Lebar={}, Tinggi={}",
            item.frame.left, item.frame.top, item.frame.width, item.frame.height
        );
    }
}

Contoh Keluaran:

[
  {
    "text": "FAKTUR #98214",
    "score": 0.992,
    "box_points": [
      [42.0, 35.0],
      [210.0, 35.0],
      [210.0, 58.0],
      [42.0, 58.0]
    ],
    "frame": { "left": 42.0, "top": 35.0, "width": 168.0, "height": 23.0 }
  },
  {
    "text": "Total Tagihan: Rp4.500.000",
    "score": 0.985,
    "box_points": [
      [42.0, 280.0],
      [265.0, 280.0],
      [265.0, 304.0],
      [42.0, 304.0]
    ],
    "frame": { "left": 42.0, "top": 280.0, "width": 223.0, "height": 24.0 }
  }
]

Contoh B: Rekonstruksi Tata Letak Spasial Dokumen

Pertahankan perataan kolom tabel, faktur, dan formulir menjadi format teks rapi:

use rusto::{DetectTextResult, ImageSource, OcrRunOptions, OutputGranularity};

let mut options = OcrRunOptions::default();
options.output = OutputGranularity::Spatial;
options.line_y_threshold = Some(0.5);
options.word_x_threshold = Some(0.4);

if let DetectTextResult::Spatial(layout_text) = ocr.detect_text(
    &ImageSource::Path("table.png".into()),
    &options,
)? {
    println!("{layout_text}");
}

Contoh Keluaran:

Deskripsi Barang           Kuantitas    Harga Satuan    Subtotal
Widget Pro X100                   2       Rp750.000  Rp1.500.000
Kabel Daya Tahan Tinggi           4       Rp125.000    Rp500.000
Braket Pemasangan                 1       Rp350.000    Rp350.000
────────────────────────────────────────────────────────────────
Total Pembayaran:                                    Rp2.350.000

Contoh C: Pemrosesan Batch Multi-Thread dengan Rayon

Karena instance RustO memiliki status sesi inferensi, gunakan instance lokal per thread atau worker pool untuk beban kerja paralel:

use rayon::prelude::*;
use rusto::{DetectTextResult, ImageSource, InitializeConfig, OcrRunOptions, RustO};
use std::path::PathBuf;

let image_paths: Vec<PathBuf> = vec![
    "doc1.jpg".into(),
    "doc2.jpg".into(),
    "doc3.jpg".into(),
    "doc4.jpg".into(),
];

// Proses secara bersamaan di beberapa inti CPU
image_paths.par_iter().for_each(|path| {
    // Setiap thread menginisialisasi sesinya sendiri yang ringan
    let config = InitializeConfig::ppv6("models/det.rten", "models/rec.rten", "models/dict.txt");
    let mut thread_ocr = RustO::initialize(config).expect("Gagal inisialisasi thread OCR");

    let result = thread_ocr.detect_text(
        &ImageSource::Path(path.clone()),
        &OcrRunOptions::default(),
    ).expect("Terjadi kesalahan OCR");

    if let DetectTextResult::Structured(lines) = result {
        println!("Selesai memproses {:?}: ditemukan {} baris", path, lines.len());
    }
});