@@ -273,67 +273,6 @@ pub fn batch_input(w: usize, chunk: &[usize], lines: &[PrepLine]) -> Vec<f32> {
273273 data
274274}
275275
276- /// Browser-path recognition batching: group lines of *differing* width into
277- /// [`REC_BATCH`]-sized chunks (sorted by width, so a chunk spans a narrow
278- /// range) and pad each chunk to its max width. The padding is the input
279- /// buffer's zero value — mid-gray in the model's `[-1, 1]` space, which is
280- /// exactly what PP-OCRv3 was trained/served with in batched inference, so the
281- /// decode matches one-at-a-time recognition (measured: 0 diffs on a mixed
282- /// Latin+Cyrillic sample). A width-ratio guard keeps padding waste ≤1.5×.
283- ///
284- /// On a text-dense page where most lines have distinct widths this collapses
285- /// N recognition calls into ≈N/[`REC_BATCH`] — a real win under ORT Web, where
286- /// every `session.run` carries thread-sync overhead. Deliberately NOT used by
287- /// the native path: that keeps the bit-identical exact-width [`width_batches`]
288- /// so the byte-for-byte conformance baseline is untouched.
289- pub fn width_batches_padded ( lines : & [ PrepLine ] ) -> Vec < ( usize , Vec < usize > ) > {
290- let mut order: Vec < usize > = ( 0 ..lines. len ( ) ) . collect ( ) ;
291- order. sort_by_key ( |& i| lines[ i] . w ) ;
292- let mut out: Vec < ( usize , Vec < usize > ) > = Vec :: new ( ) ;
293- let mut cur: Vec < usize > = Vec :: new ( ) ;
294- let mut cur_min = 0usize ;
295- let flush = |cur : & mut Vec < usize > , out : & mut Vec < ( usize , Vec < usize > ) > | {
296- if !cur. is_empty ( ) {
297- let mw = cur. iter ( ) . map ( |& j| lines[ j] . w ) . max ( ) . unwrap_or ( 0 ) ;
298- out. push ( ( mw, std:: mem:: take ( cur) ) ) ;
299- }
300- } ;
301- for & i in & order {
302- let w = lines[ i] . w ;
303- // Cap padding waste: start a new chunk if this line is >1.5× the
304- // chunk's narrowest, or the chunk is already full.
305- if cur. len ( ) == REC_BATCH || ( !cur. is_empty ( ) && w * 2 > cur_min * 3 ) {
306- flush ( & mut cur, & mut out) ;
307- }
308- if cur. is_empty ( ) {
309- cur_min = w;
310- }
311- cur. push ( i) ;
312- }
313- flush ( & mut cur, & mut out) ;
314- out
315- }
316-
317- /// Pack a padded width-batch (see [`width_batches_padded`]): each line's CHW
318- /// data goes into the left `line.w` columns of a zero-filled `(N, 3, H, W)`
319- /// buffer, `W` = the batch's max width.
320- pub fn batch_input_padded ( w : usize , chunk : & [ usize ] , lines : & [ PrepLine ] ) -> Vec < f32 > {
321- let h = REC_HEIGHT as usize ;
322- let mut data = vec ! [ 0f32 ; chunk. len( ) * 3 * h * w] ;
323- for ( i, & ix) in chunk. iter ( ) . enumerate ( ) {
324- let lw = lines[ ix] . w ;
325- let src = & lines[ ix] . data ; // 3 * h * lw, CHW
326- for c in 0 ..3 {
327- for y in 0 ..h {
328- let s = c * h * lw + y * lw;
329- let d = i * 3 * h * w + c * h * w + y * w;
330- data[ d..d + lw] . copy_from_slice ( & src[ s..s + lw] ) ;
331- }
332- }
333- }
334- data
335- }
336-
337276#[ cfg( test) ]
338277mod tests {
339278 use super :: * ;
@@ -402,33 +341,4 @@ mod tests {
402341 ] ;
403342 assert_eq ! ( decode_row( & chars, & probs, 4 ) , "ab" ) ;
404343 }
405-
406- /// Two lines of different width batch together when padded, and the padded
407- /// buffer places each line's data left-aligned with a zero tail.
408- #[ test]
409- fn padded_batching_groups_and_left_aligns ( ) {
410- // Narrow line (w=8, all 1.0) + wide line (w=10, all 2.0), within the
411- // 1.5x width-ratio guard so they share one padded batch.
412- let lines = vec ! [
413- PrepLine { w: 8 , data: vec![ 1.0 ; 3 * REC_HEIGHT as usize * 8 ] } ,
414- PrepLine { w: 10 , data: vec![ 2.0 ; 3 * REC_HEIGHT as usize * 10 ] } ,
415- ] ;
416- let batches = width_batches_padded ( & lines) ;
417- assert_eq ! ( batches. len( ) , 1 , "same-ratio widths share a padded batch" ) ;
418- let ( w, chunk) = & batches[ 0 ] ;
419- assert_eq ! ( * w, 10 , "batch width is the max in the chunk" ) ;
420- let buf = batch_input_padded ( * w, chunk, & lines) ;
421- assert_eq ! ( buf. len( ) , chunk. len( ) * 3 * REC_HEIGHT as usize * 10 ) ;
422- // First row of the narrow line: 8 ones then 2 zero-pad columns.
423- let narrow_ix = chunk. iter ( ) . position ( |& i| lines[ i] . w == 8 ) . unwrap ( ) ;
424- let row = & buf[ narrow_ix * 3 * REC_HEIGHT as usize * 10 ..] [ ..10 ] ;
425- assert_eq ! ( row, & [ 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 0.0 , 0.0 ] ) ;
426-
427- // A line >1.5x the chunk minimum starts a fresh batch (bounds waste).
428- let spread = vec ! [
429- PrepLine { w: 8 , data: vec![ 0.0 ; 3 * REC_HEIGHT as usize * 8 ] } ,
430- PrepLine { w: 40 , data: vec![ 0.0 ; 3 * REC_HEIGHT as usize * 40 ] } ,
431- ] ;
432- assert_eq ! ( width_batches_padded( & spread) . len( ) , 2 ) ;
433- }
434344}
0 commit comments