Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package ocrs for openSUSE:Factory checked in at 2024-06-05 17:42:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ocrs (Old)
and /work/SRC/openSUSE:Factory/.ocrs.new.24587 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ocrs"
Wed Jun 5 17:42:49 2024 rev:3 rq:1178678 version:0.8.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/ocrs/ocrs.changes 2024-05-20 18:14:24.640417779 +0200
+++ /work/SRC/openSUSE:Factory/.ocrs.new.24587/ocrs.changes 2024-06-05 17:43:29.920505706 +0200
@@ -1,0 +2,11 @@
+Wed Jun 5 07:53:14 UTC 2024 - Muhammad Akbar Yanuar Mantari
+
+- Update to version 0.8.0
+ * Updated rten to v0.10.0: This improves performance when
+ recognizing long lines of text and improves efficiency by
+ setting the number of threads to match the number of physical
+ cores.
+ * Errors that occur when running the text recognition model are
+ now propagated to the caller instead of causing a panic.
+
+-------------------------------------------------------------------
Old:
----
ocrs-ocrs-v0.7.0.tar.gz
New:
----
ocrs-ocrs-v0.8.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ ocrs.spec ++++++
--- /var/tmp/diff_new_pack.GJtFtM/_old 2024-06-05 17:43:30.872540377 +0200
+++ /var/tmp/diff_new_pack.GJtFtM/_new 2024-06-05 17:43:30.876540523 +0200
@@ -22,7 +22,7 @@
%bcond_without test
%endif
Name: ocrs
-Version: 0.7.0
+Version: 0.8.0
Release: 0
Summary: A modern OCR engine written in Rust
License: Apache-2.0 AND MIT
++++++ ocrs-ocrs-v0.7.0.tar.gz -> ocrs-ocrs-v0.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/CHANGELOG.md new/ocrs-ocrs-v0.8.0/CHANGELOG.md
--- old/ocrs-ocrs-v0.7.0/CHANGELOG.md 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/CHANGELOG.md 2024-05-25 10:41:11.000000000 +0200
@@ -5,6 +5,26 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.8.0] - 2024-05-25
+
+### Breaking changes
+
+This release changes Ocrs's internal use of threads, which may affect consumers
+that are using their own parallelism. Specifically Ocrs no longer uses the
+global Rayon thread pool but instead a custom thread pool which is sized to
+match the number of physical rather than logical cores. See
+https://github.com/robertknight/ocrs/pull/79 for more details and information on
+adapting.
+
+### Changes
+
+- Updated rten to v0.10.0. This improves performance when recognizing long lines
+ of text (https://github.com/robertknight/ocrs/pull/79) and improves efficiency
+ by setting the number of threads to match the number of physical cores.
+
+- Errors that occur when running the text recognition model are now propagated
+ to the caller instead of causing a panic (https://github.com/robertknight/ocrs/pull/77)
+
## [0.7.0] - 2024-05-16
### Breaking changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/Cargo.lock new/ocrs-ocrs-v0.8.0/Cargo.lock
--- old/ocrs-ocrs-v0.7.0/Cargo.lock 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/Cargo.lock 2024-05-25 10:41:11.000000000 +0200
@@ -173,6 +173,12 @@
]
[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -266,8 +272,18 @@
]
[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
name = "ocrs"
-version = "0.7.0"
+version = "0.8.0"
dependencies = [
"anyhow",
"fastrand",
@@ -283,7 +299,7 @@
[[package]]
name = "ocrs-cli"
-version = "0.7.0"
+version = "0.8.0"
dependencies = [
"anyhow",
"home",
@@ -378,13 +394,15 @@
[[package]]
name = "rten"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb9d6d80601e57cab46f477955be6e3be1a4c92ed0aebb3376e1f19d24e83bb1"
+checksum = "09c030cdf90e64c5eeeba389ca59da14b0a106b1b8366c15591251bb6a2e777f"
dependencies = [
"flatbuffers",
"libm",
+ "num_cpus",
"rayon",
+ "rten-simd",
"rten-tensor",
"rten-vecmath",
"rustc-hash",
@@ -394,27 +412,36 @@
[[package]]
name = "rten-imageproc"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "529fdef25f8232ebb08fb6cfc785ec97a7fb268bebc4895e36e8750e2bbeaa51"
+checksum = "5ba61077269b2b2c90445bfd55fb798dcd544b56e7fd78faaea51940b8e429ae"
dependencies = [
"rten-tensor",
]
[[package]]
+name = "rten-simd"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb16da64e0d08ce56dc17d8304ab2da541176ee30430c0b0e581a7841a660ae"
+
+[[package]]
name = "rten-tensor"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffa78180a98337a43163e9da8f202120e9ae3b82366cccfb05a5a854e48cd581"
+checksum = "52f5e53d2e43bb736e89e4ea41b707e024190f8ba47c3eddf5a3c2d022089909"
dependencies = [
"smallvec",
]
[[package]]
name = "rten-vecmath"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "495f48d459768d61ca37b418f79ac7aac3a707024c79fa49a14dd2c1ad8a2c0e"
+checksum = "56eccc46a7e7a2df2cebb7ba95e613a01942a01e0f2f2f7d6122176ab7372e9f"
+dependencies = [
+ "rten-simd",
+]
[[package]]
name = "rustc-hash"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/Cargo.toml new/ocrs-ocrs-v0.8.0/Cargo.toml
--- old/ocrs-ocrs-v0.7.0/Cargo.toml 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/Cargo.toml 2024-05-25 10:41:11.000000000 +0200
@@ -4,3 +4,8 @@
"ocrs",
"ocrs-cli",
]
+
+[workspace.dependencies]
+rten = { version = "0.10.0" }
+rten-imageproc = { version = "0.10.0" }
+rten-tensor = { version = "0.10.0" }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/Makefile new/ocrs-ocrs-v0.8.0/Makefile
--- old/ocrs-ocrs-v0.7.0/Makefile 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/Makefile 2024-05-25 10:41:11.000000000 +0200
@@ -41,5 +41,12 @@
wasm-bindgen target/wasm32-unknown-unknown/release/ocrs.wasm --out-dir js/dist/ --target web --reference-types --weak-refs
tools/optimize-wasm.sh js/dist/ocrs_bg.wasm
+# Build Ocrs CLI for non-browser WebAssembly runtimes (eg. wasmtime). Run using:
+#
+# wasmtime --dir . target/wasm32-wasi/release/ocrs.wasm --detect-model text-detection.rten --rec-model text-recognition.rten ocrs-cli/test-data/why-rust.png
+.PHONY: wasm-wasi
+wasm-wasi:
+ RUSTFLAGS="-C target-feature=+simd128" cargo build --release --target wasm32-wasi --package ocrs-cli
+
.PHONY: wasm-all
wasm-all: wasm wasm-nosimd
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs/Cargo.toml new/ocrs-ocrs-v0.8.0/ocrs/Cargo.toml
--- old/ocrs-ocrs-v0.7.0/ocrs/Cargo.toml 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/ocrs/Cargo.toml 2024-05-25 10:41:11.000000000 +0200
@@ -1,6 +1,6 @@
[package]
name = "ocrs"
-version = "0.7.0"
+version = "0.8.0"
edition = "2021"
authors = ["Robert Knight"]
description = "OCR engine"
@@ -11,9 +11,9 @@
[dependencies]
anyhow = "1.0.80"
rayon = "1.10.0"
-rten = { version = "0.9.0" }
-rten-imageproc = { version = "0.9.0" }
-rten-tensor = { version = "0.9.0" }
+rten = { workspace = true }
+rten-imageproc = { workspace = true }
+rten-tensor = { workspace = true }
thiserror = "1.0.59"
[target.'cfg(target_arch = "wasm32")'.dependencies]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs/src/errors.rs new/ocrs-ocrs-v0.8.0/ocrs/src/errors.rs
--- old/ocrs-ocrs-v0.7.0/ocrs/src/errors.rs 1970-01-01 01:00:00.000000000 +0100
+++ new/ocrs-ocrs-v0.8.0/ocrs/src/errors.rs 2024-05-25 10:41:11.000000000 +0200
@@ -0,0 +1,25 @@
+use std::error::Error;
+use std::fmt;
+
+/// The error type returned when running a machine learning model fails.
+#[derive(Debug)]
+pub enum ModelRunError {
+ /// Model execution failed.
+ RunFailed(Box),
+
+ /// The model output had a different data type or shape than expected.
+ WrongOutput,
+}
+
+impl fmt::Display for ModelRunError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ ModelRunError::RunFailed(err) => write!(f, "model run failed: {}", err),
+ ModelRunError::WrongOutput => {
+ write!(f, "model output had unexpected type or shape")
+ }
+ }
+ }
+}
+
+impl Error for ModelRunError {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs/src/lib.rs new/ocrs-ocrs-v0.8.0/ocrs/src/lib.rs
--- old/ocrs-ocrs-v0.7.0/ocrs/src/lib.rs 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/ocrs/src/lib.rs 2024-05-25 10:41:11.000000000 +0200
@@ -5,6 +5,7 @@
use rten_tensor::NdTensor;
mod detection;
+mod errors;
mod geom_util;
mod layout_analysis;
mod log;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs/src/recognition.rs new/ocrs-ocrs-v0.8.0/ocrs/src/recognition.rs
--- old/ocrs-ocrs-v0.7.0/ocrs/src/recognition.rs 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/ocrs/src/recognition.rs 2024-05-25 10:41:11.000000000 +0200
@@ -3,13 +3,14 @@
use anyhow::anyhow;
use rayon::prelude::*;
use rten::ctc::{CtcDecoder, CtcHypothesis};
-use rten::{Dimension, FloatOperators, Model, NodeId};
+use rten::{thread_pool, Dimension, FloatOperators, Model, NodeId};
use rten_imageproc::{
bounding_rect, BoundingRect, Line, Point, PointF, Polygon, Rect, RotatedRect,
};
use rten_tensor::prelude::*;
use rten_tensor::{NdTensor, NdTensorView, Tensor};
+use crate::errors::ModelRunError;
use crate::geom_util::{downwards_line, leftmost_edge, rightmost_edge};
use crate::preprocess::BLACK_VALUE;
use crate::text_items::{TextChar, TextLine};
@@ -359,12 +360,14 @@
/// Run text recognition on an NCHW batch of text line images, and return
/// a `[batch, seq, label]` tensor of class probabilities.
- fn run(&self, input: NdTensor) -> anyhow::Result> {
+ fn run(&self, input: NdTensor) -> Result, ModelRunError> {
let input: Tensor<f32> = input.into();
- let [output] =
- self.model
- .run_n(&[(self.input_id, (&input).into())], [self.output_id], None)?;
- let mut rec_sequence: NdTensor = output.try_into()?;
+ let [output] = self
+ .model
+ .run_n(&[(self.input_id, (&input).into())], [self.output_id], None)
+ .map_err(|err| ModelRunError::RunFailed(err.into()))?;
+ let mut rec_sequence: NdTensor =
+ output.try_into().map_err(|_| ModelRunError::WrongOutput)?;
// Transpose from [seq, batch, class] => [batch, seq, class]
rec_sequence.permute([1, 0, 2]);
@@ -470,52 +473,59 @@
.collect();
// Run text recognition on batches of lines.
- let mut line_rec_results: Vec<LineRecResult> = line_groups
- .into_par_iter()
- .flat_map(|(group_width, lines)| {
- if debug {
- println!(
- "Processing group of {} lines of width {}",
- lines.len(),
- group_width,
- );
- }
-
- let rec_input = prepare_text_line_batch(
- &image,
- &lines,
- page_rect,
- rec_img_height as usize,
- group_width as usize,
- );
-
- // TODO - Propagate errors from recognition model to caller.
- let rec_output = self.run(rec_input).expect("recognition failed");
- let ctc_input_len = rec_output.shape()[1];
-
- // Apply CTC decoding to get the label sequence for each line.
- lines
- .into_iter()
- .enumerate()
- .map(|(group_line_index, line)| {
- let decoder = CtcDecoder::new();
- let input_seq = rec_output.slice([group_line_index]);
- let ctc_output = match decode_method {
- DecodeMethod::Greedy => decoder.decode_greedy(input_seq),
- DecodeMethod::BeamSearch { width } => {
- decoder.decode_beam(input_seq, width)
- }
- };
- LineRecResult {
- line,
- rec_input_len: group_width as usize,
- ctc_input_len,
- ctc_output,
+ let batch_rec_results: Result, ModelRunError> =
+ thread_pool().run(|| {
+ line_groups
+ .into_par_iter()
+ .map(|(group_width, lines)| {
+ if debug {
+ println!(
+ "Processing group of {} lines of width {}",
+ lines.len(),
+ group_width,
+ );
}
+
+ let rec_input = prepare_text_line_batch(
+ &image,
+ &lines,
+ page_rect,
+ rec_img_height as usize,
+ group_width as usize,
+ );
+
+ let rec_output = self.run(rec_input)?;
+ let ctc_input_len = rec_output.shape()[1];
+
+ // Apply CTC decoding to get the label sequence for each line.
+ let line_rec_results = lines
+ .into_iter()
+ .enumerate()
+ .map(|(group_line_index, line)| {
+ let decoder = CtcDecoder::new();
+ let input_seq = rec_output.slice([group_line_index]);
+ let ctc_output = match decode_method {
+ DecodeMethod::Greedy => decoder.decode_greedy(input_seq),
+ DecodeMethod::BeamSearch { width } => {
+ decoder.decode_beam(input_seq, width)
+ }
+ };
+ LineRecResult {
+ line,
+ rec_input_len: group_width as usize,
+ ctc_input_len,
+ ctc_output,
+ }
+ })
+ .collect::>();
+
+ Ok(line_rec_results)
})
- .collect::>()
- })
- .collect();
+ .collect()
+ });
+
+ let mut line_rec_results: Vec<LineRecResult> =
+ batch_rec_results?.into_iter().flatten().collect();
// The recognition outputs are in a different order than the inputs due to
// batching and parallel processing. Re-sort them into input order.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs-cli/Cargo.toml new/ocrs-ocrs-v0.8.0/ocrs-cli/Cargo.toml
--- old/ocrs-ocrs-v0.7.0/ocrs-cli/Cargo.toml 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/ocrs-cli/Cargo.toml 2024-05-25 10:41:11.000000000 +0200
@@ -1,6 +1,6 @@
[package]
name = "ocrs-cli"
-version = "0.7.0"
+version = "0.8.0"
edition = "2021"
authors = ["Robert Knight"]
description = "OCR CLI tool for extracting text from images"
@@ -12,16 +12,18 @@
image = { version = "0.25.1", default-features = false, features = ["png", "jpeg", "webp"] }
png = "0.17.6"
serde_json = "1.0.116"
-rten = { version = "0.9.0" }
-rten-imageproc = { version = "0.9.0" }
-rten-tensor = { version = "0.9.0" }
-ocrs = { path = "../ocrs", version = "0.7.0" }
+rten = { workspace = true }
+rten-imageproc = { workspace = true }
+rten-tensor = { workspace = true }
+ocrs = { path = "../ocrs", version = "0.8.0" }
lexopt = "0.3.0"
-ureq = "2.9.7"
url = "2.4.0"
-home = "0.5.9"
anyhow = "1.0.79"
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+ureq = "2.9.7"
+home = "0.5.9"
+
[features]
# Use AVX-512 instructions if available. Requires nightly Rust.
avx512 = ["rten/avx512"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ocrs-ocrs-v0.7.0/ocrs-cli/src/models.rs new/ocrs-ocrs-v0.8.0/ocrs-cli/src/models.rs
--- old/ocrs-ocrs-v0.7.0/ocrs-cli/src/models.rs 2024-05-16 10:41:41.000000000 +0200
+++ new/ocrs-ocrs-v0.8.0/ocrs-cli/src/models.rs 2024-05-25 10:41:11.000000000 +0200
@@ -1,13 +1,18 @@
use std::fmt;
-use std::fs;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
+
+#[cfg(not(target_arch = "wasm32"))]
+use std::{fs, path::Path};
use anyhow::anyhow;
use rten::Model;
+
+#[cfg(not(target_arch = "wasm32"))]
use url::Url;
/// Return the path to the directory in which cached models etc. should be
/// saved.
+#[cfg(not(target_arch = "wasm32"))]
fn cache_dir() -> Result {
let mut cache_dir: PathBuf =
home::home_dir().ok_or(anyhow!("Failed to determine home directory"))?;
@@ -22,6 +27,7 @@
/// Extract the last path segment from a URL.
///
/// eg. "https://models.com/text-detection.rten" => "text-detection.rten".
+#[cfg(not(target_arch = "wasm32"))]
#[allow(rustdoc::bare_urls)]
fn filename_from_url(url: &str) -> Option<String> {
let parsed = Url::parse(url).ok()?;
@@ -33,6 +39,7 @@
/// Download a file from `url` to a local cache, if not already fetched, and
/// return the path to the local file.
+#[cfg(not(target_arch = "wasm32"))]
fn download_file(url: &str, filename: Option<&str>) -> Result {
let cache_dir = cache_dir()?;
let filename = match filename {
@@ -55,6 +62,13 @@
Ok(file_path)
}
+#[cfg(target_arch = "wasm32")]
+fn download_file(_url: &str, _filename: Option<&str>) -> Result {
+ Err(anyhow!(
+ "Downloading models from a URL is not supported on the current platform"
+ ))
+}
+
/// Location that a model can be loaded from.
#[derive(Clone, Copy)]
pub enum ModelSource<'a> {
++++++ vendor.tar.zst ++++++
/work/SRC/openSUSE:Factory/ocrs/vendor.tar.zst /work/SRC/openSUSE:Factory/.ocrs.new.24587/vendor.tar.zst differ: char 255581, line 1058