pub struct LineSegmenter { /* private fields */ }Expand description
Line segmenter using horizontal projection profile
This segmenter detects text lines in a document image by analyzing the horizontal projection profile - the sum of dark pixels in each row.
§Algorithm
- Convert image to grayscale and binarize (threshold at 128)
- Suppress printed rules — page borders, table rules, underlines — so their
ink floor cannot hide every gap; see
suppress_page_rules - Compute horizontal projection profile (sum of dark pixels per row)
- Apply smoothing to reduce noise
- Find gaps between text regions (where projection is near zero)
- Extract each text region as a separate line
§Parameters
min_line_height: Minimum height to consider as a valid text linesmooth_window: Window size for smoothing the projection profiledensity_threshold_ratio: Fraction of mean row density that still counts as a gap
Implementations§
Source§impl LineSegmenter
impl LineSegmenter
Sourcepub fn new(min_line_height: u32, smooth_window: u32) -> Self
pub fn new(min_line_height: u32, smooth_window: u32) -> Self
Create a new line segmenter with specified parameters
§Arguments
min_line_height- Minimum height in pixels to consider as a valid text linesmooth_window- Window size for smoothing the projection profile (1 = no smoothing)
§Returns
A new LineSegmenter instance
§Example
use monocr_onnx::segmenter::LineSegmenter;
// Create segmenter with default parameters
let segmenter = LineSegmenter::new(10, 3);Sourcepub fn with_density_ratio(
min_line_height: u32,
smooth_window: u32,
density_threshold_ratio: f32,
) -> Self
pub fn with_density_ratio( min_line_height: u32, smooth_window: u32, density_threshold_ratio: f32, ) -> Self
Create a segmenter with an explicit gap threshold ratio.
See crate::MonOcrBuilder::density_threshold_ratio for what the ratio
does and why it is worth setting per input class. The caller is
responsible for passing a finite, positive ratio; the builder validates
it.
Sourcepub fn segment(&self, image_path: impl AsRef<Path>) -> Result<Vec<LineSegment>>
pub fn segment(&self, image_path: impl AsRef<Path>) -> Result<Vec<LineSegment>>
Segment an image into text lines
This is the main method that performs line segmentation on a document image. It uses horizontal projection profile analysis to detect text lines.
§Arguments
image_path- Path to the image file
§Returns
Ok(Vec<LineSegment>)- Vector of segmented lines with images and bounding boxesErr(anyhow::Error)- If the image cannot be opened or processed
§Algorithm Details
- Binarization: Convert to grayscale and threshold at 128 (pixels < 128 are text)
- Rule suppression: Remove printed rules, so a page border cannot
fuse the whole page into one band (
suppress_page_rules) - Projection: Compute horizontal projection profile (sum of text pixels per row)
- Smoothing: Apply moving average filter if smooth_window > 1
- Gap Detection: Find gaps where the RAW projection is below
density_threshold_ratioof the SMOOTHED profile’s mean non-empty row density (default 5%). The two profiles are deliberately different: the smoothed mean is the steadier calibration, and the raw profile is the only one that still reaches zero between tightly set lines - Line Extraction: Extract each region between gaps as a separate line
- Padding: Add 4-pixel padding around each line for edge character capture
§Polarity
The threshold treats dark as ink, so a light-on-dark page must be
inverted before it reaches here or the BACKGROUND is what gets segmented.
crate::normalize_polarity is that step and MonOcr::predict_page
runs it. This method does not, because it is also the entry point for a
caller who has already corrected polarity.
Sourcepub fn segment_image(&self, gray_img: &GrayImage) -> Result<Vec<LineSegment>>
pub fn segment_image(&self, gray_img: &GrayImage) -> Result<Vec<LineSegment>>
Segment an image that is already decoded and grayscale.
The path-taking Self::segment is a thin wrapper over this. The split
exists because polarity has to be corrected BEFORE segmentation — the
threshold below treats dark as ink, so a light-on-dark page segments the
BACKGROUND and returns the gaps between lines — and the caller doing that
correction is holding an image, not a path. go/monocr.go’s
predictImage and js/src/monocr.js’s normalizePageForSegmentation
are the same arrangement.
Auto Trait Implementations§
impl Freeze for LineSegmenter
impl RefUnwindSafe for LineSegmenter
impl Send for LineSegmenter
impl Sync for LineSegmenter
impl Unpin for LineSegmenter
impl UnsafeUnpin for LineSegmenter
impl UnwindSafe for LineSegmenter
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more