@@ -412,50 +412,51 @@ pub fn row_running_sum(image: &GrayImage, row: u32, buffer: &mut [u32], padding:
412412/// ```
413413pub fn column_running_sum ( image : & GrayImage , column : u32 , buffer : & mut [ u32 ] , padding : u32 ) {
414414 // TODO: faster, more formats
415- let ( width, height) = image. dimensions ( ) ;
416- assert ! (
417- // assertion 1
418- buffer. len( ) >= height as usize + 2 * padding as usize ,
419- "Buffer length {} is less than {} + 2 * {}" ,
420- buffer. len( ) ,
421- height,
422- padding
423- ) ;
415+
416+ assert ! ( image. height( ) > 0 , "image is empty" ) ;
424417 assert ! (
425- // assertion 2
426- column < width,
418+ // assertion `a`
419+ column < image . width( ) ,
427420 "column out of bounds: {} >= {}" ,
428421 column,
429- width
422+ image . width( )
430423 ) ;
424+
425+ let height = usize:: try_from ( image. height ( ) ) . unwrap ( ) ;
426+ let padding = usize:: try_from ( padding) . unwrap ( ) ;
431427 assert ! (
432- // assertion 3
433- height > 0 ,
434- "image is empty"
428+ buffer. len( ) >= height + 2 * padding,
429+ "Buffer length {} is less than {} + 2 * {}" ,
430+ buffer. len( ) ,
431+ height,
432+ padding
435433 ) ;
434+ let buffer = & mut buffer[ ..height + 2 * padding] ;
435+ let ( head, rest) = buffer. split_at_mut ( padding) ;
436+ let ( middle, tail) = rest. split_at_mut ( height) ;
437+ debug_assert_eq ! ( padding, head. len( ) ) ;
438+ debug_assert_eq ! ( height, middle. len( ) , ) ;
439+ debug_assert_eq ! ( padding, tail. len( ) ) ;
436440
437441 let first = image. get_pixel ( column, 0 ) [ 0 ] as u32 ;
438- let last = image. get_pixel ( column, height - 1 ) [ 0 ] as u32 ;
442+ let last = image. get_pixel ( column, image . height ( ) - 1 ) [ 0 ] as u32 ;
439443
440444 let mut sum = 0 ;
441-
442- for b in & mut buffer[ ..padding as usize ] {
445+ for b in head {
443446 sum += first;
444447 * b = sum;
445448 }
446- // JUSTIFICATION:
447- // Benefit
448- // Using checked indexing here makes this function take 1.8x longer, as measured by bench_column_running_sum
449- // Correctness
450- // column is in bounds due to assertion 2.
451- // height + padding - 1 < buffer.len() due to assertions 1 and 3.
452- unsafe {
453- for y in 0 ..height {
454- sum += image. unsafe_get_pixel ( column, y) [ 0 ] as u32 ;
455- * buffer. get_unchecked_mut ( y as usize + padding as usize ) = sum;
456- }
449+ for ( y, b) in ( 0 ..image. height ( ) ) . zip ( middle) {
450+ // JUSTIFICATION:
451+ // Benefit
452+ // Using checked indexing here makes this function take 1.53x longer, as measured by bench_column_running_sum
453+ // Correctness
454+ // column is in bounds due to assertion `a`.
455+ // y is in bounds by construction.
456+ sum += unsafe { image. unsafe_get_pixel ( column, y) } [ 0 ] as u32 ;
457+ * b = sum;
457458 }
458- for b in & mut buffer [ padding as usize + height as usize .. ] {
459+ for b in tail {
459460 sum += last;
460461 * b = sum;
461462 }
@@ -551,6 +552,28 @@ mod tests {
551552 assert_eq ! ( sum_image_pixels( & integral, 1 , 1 , 1 , 1 ) , [ 10 , 11 , 12 ] ) ;
552553 }
553554
555+ #[ test]
556+ fn test_column_running_sum ( ) {
557+ let get_column_running_sum = |image : & GrayImage , column, padding| -> Vec < u32 > {
558+ let mut buffer = vec ! [ 0u32 ; ( image. height( ) + 2 * padding) as usize ] ;
559+ column_running_sum ( image, column, & mut buffer, padding) ;
560+ buffer
561+ } ;
562+
563+ let padding = 0 ;
564+ let img = gray_image ! ( type : u8 ,
565+ 1 , 2 ;
566+ 3 , 4 ;
567+ 5 , 6
568+ ) ;
569+ assert_eq ! ( get_column_running_sum( & img, 0 , padding) , [ 1 , 4 , 9 , ] ) ;
570+ assert_eq ! ( get_column_running_sum( & img, 1 , padding) , [ 2 , 6 , 12 , ] ) ;
571+
572+ let padding = 1 ;
573+ assert_eq ! ( get_column_running_sum( & img, 0 , padding) , [ 1 , 2 , 5 , 10 , 15 ] ) ;
574+ assert_eq ! ( get_column_running_sum( & img, 1 , padding) , [ 2 , 4 , 8 , 14 , 20 ] ) ;
575+ }
576+
554577 /// Simple implementation of integral_image to validate faster versions against.
555578 pub fn integral_image_ref < I > ( image : & I ) -> Image < Luma < u32 > >
556579 where
@@ -595,6 +618,65 @@ mod proptests {
595618
596619 assert_eq!( expected, actual) ;
597620 }
621+
622+ #[ test]
623+ fn proptest_column_running_sum(
624+ image in arbitrary_image:: <Luma <u8 >>( 0 ..10 , 1 ..10 ) ,
625+ padding in 0 ..10u32 ,
626+ ) {
627+ assert!( image. height( ) > 0 ) ;
628+ let mut actual = vec![ 0u32 ; ( image. height( ) + 2 * padding) as usize ] ;
629+ let mut expected = actual. clone( ) ;
630+
631+ for col in 0 ..image. width( ) {
632+ actual. fill( 0 ) ;
633+ expected. fill( 0 ) ;
634+
635+ column_running_sum( & image, col, & mut actual, padding) ;
636+ column_running_sum_reference( & image, col, & mut expected, padding) ;
637+ assert_eq!( actual, expected) ;
638+ }
639+ }
640+ }
641+
642+ pub fn column_running_sum_reference (
643+ image : & GrayImage ,
644+ column : u32 ,
645+ buffer : & mut [ u32 ] ,
646+ padding : u32 ,
647+ ) {
648+ let ( width, height) = image. dimensions ( ) ;
649+ assert ! (
650+ buffer. len( ) >= height as usize + 2 * padding as usize ,
651+ "Buffer length {} is less than {} + 2 * {}" ,
652+ buffer. len( ) ,
653+ height,
654+ padding
655+ ) ;
656+ assert ! (
657+ column < width,
658+ "column out of bounds: {} >= {}" ,
659+ column,
660+ width
661+ ) ;
662+ assert ! ( height > 0 , "image is empty" ) ;
663+
664+ let first = image. get_pixel ( column, 0 ) [ 0 ] as u32 ;
665+ let last = image. get_pixel ( column, height - 1 ) [ 0 ] as u32 ;
666+
667+ let mut sum = 0 ;
668+ for b in & mut buffer[ ..padding as usize ] {
669+ sum += first;
670+ * b = sum;
671+ }
672+ for y in 0 ..height {
673+ sum += image. get_pixel ( column, y) [ 0 ] as u32 ;
674+ buffer[ y as usize + padding as usize ] = sum;
675+ }
676+ for b in & mut buffer[ padding as usize + height as usize ..] {
677+ sum += last;
678+ * b = sum;
679+ }
598680 }
599681}
600682
0 commit comments