1919
2020import static org .junit .Assert .assertTrue ;
2121
22+ import java .awt .image .BufferedImage ;
23+ import java .awt .image .PixelGrabber ;
24+ import java .io .ByteArrayInputStream ;
25+ import java .net .URL ;
26+ import java .nio .ByteBuffer ;
27+ import java .nio .channels .Channels ;
28+ import java .nio .channels .ReadableByteChannel ;
29+
30+ import javax .imageio .ImageIO ;
31+
2232import org .junit .Test ;
2333
2434import net .imglib2 .Cursor ;
3141import net .imglib2 .img .cell .CellImg ;
3242import net .imglib2 .img .cell .CellImgFactory ;
3343import net .imglib2 .type .numeric .ARGBType ;
44+ import net .imglib2 .type .numeric .RealType ;
3445import net .imglib2 .type .numeric .integer .LongType ;
3546import net .imglib2 .type .numeric .integer .UnsignedByteType ;
3647import net .imglib2 .type .numeric .integer .UnsignedShortType ;
@@ -310,31 +321,75 @@ protected boolean testIfThenElse() {
310321 var ( "max" ) ) ) ) )
311322 .into ( saturation );
312323
313- float sum = 0 ;
314-
315- for ( final FloatType t : saturation )
316- sum += t .get ();
324+ final long sum = sumAsInts ( saturation , 255.0 );
317325
318326 // Ignore floating-point error
319- System .out .println ( "IfThenElse: saturation sum is " + ( int ) sum + ", expected: " + ( int )( saturation_sum / 255.0f ) );
327+ System .out .println ( "IfThenElse: saturation sum is " + sum + ", expected: " + saturation_sum );
320328
321- return ( int ) sum == ( int )( saturation_sum / 255.0f ) ;
329+ return sum == saturation_sum ;
322330 }
323331
324- protected boolean testSaturationPerformance ( final int n_iterations , final boolean print ) {
325- final int [] leaf_crop_pix = new int []{-7430893 , -6115530 , -4275105 , -3354241 , -2960497 , -3026034 , -4012171 , -4735905 , -4538526 , -3814794 , -8219392 , -7101930 , -5589438 , -3946389 , -3223163 , -3419766 , -4538005 , -5524656 , -5589681 , -4735134 , -8153344 , -7890174 , -6773213 , -5392819 , -4406935 , -4274320 , -5260715 , -6247114 , -6444498 , -6181573 , -7890176 , -8087552 , -7562481 , -6576333 , -5590446 , -5918133 , -6509772 , -7429350 , -7495918 , -7364580 , -7955694 , -8415744 , -8349952 , -8086528 , -7626752 , -7560684 , -7955442 , -8284407 , -8218869 , -7955692 , -7692270 , -8022011 , -8219390 , -8350201 , -8022262 , -8021490 , -8350200 , -8547579 , -8481786 , -8087283 , -7561208 , -7956218 , -8351225 , -8680439 , -8812276 , -8811001 , -8876796 , -8942336 , -8744959 , -8415994 , -8087552 , -8087552 , -8679166 , -9075199 , -9536000 , -9337600 , -9271808 , -9139968 , -8876544 , -8547584 , -8679168 , -8415744 , -8415744 , -9008384 , -9666560 , -9666816 , -9534976 , -9205760 , -8942336 , -8679168 , -8548352 , -8087552 , -8086528 , -8547328 , -9074176 , -9732608 , -9534976 , -9271552 , -8942592 , -8744960 };
326- final long saturation_sum = 21367 ;
332+ /**
333+ * Emulate what ImageJ ij.process.ColorProcessor does: multiply by 255.0d and cast to int.
334+ *
335+ * @param rai
336+ * @param factor
337+ * @return
338+ */
339+ static private final < O extends RealType < O > > long sumAsInts ( final RandomAccessibleInterval < O > rai , final double factor )
340+ {
341+ long sum = 0 ;
327342
328- final long [] dims = new long []{ 10 , 10 };
343+ for ( final O t : Views .iterable ( rai ) )
344+ sum += (int )(t .getRealFloat () * factor );
345+
346+ return sum ;
347+ }
348+
349+ protected boolean testSaturationPerformance ( final int n_iterations , final boolean print )
350+ {
351+ // Small cut out
352+ //final int[] leaf_pix = new int[]{-7430893, -6115530, -4275105, -3354241, -2960497, -3026034, -4012171, -4735905, -4538526, -3814794, -8219392, -7101930, -5589438, -3946389, -3223163, -3419766, -4538005, -5524656, -5589681, -4735134, -8153344, -7890174, -6773213, -5392819, -4406935, -4274320, -5260715, -6247114, -6444498, -6181573, -7890176, -8087552, -7562481, -6576333, -5590446, -5918133, -6509772, -7429350, -7495918, -7364580, -7955694, -8415744, -8349952, -8086528, -7626752, -7560684, -7955442, -8284407, -8218869, -7955692, -7692270, -8022011, -8219390, -8350201, -8022262, -8021490, -8350200, -8547579, -8481786, -8087283, -7561208, -7956218, -8351225, -8680439, -8812276, -8811001, -8876796, -8942336, -8744959, -8415994, -8087552, -8087552, -8679166, -9075199, -9536000, -9337600, -9271808, -9139968, -8876544, -8547584, -8679168, -8415744, -8415744, -9008384, -9666560, -9666816, -9534976, -9205760, -8942336, -8679168, -8548352, -8087552, -8086528, -8547328, -9074176, -9732608, -9534976, -9271552, -8942592, -8744960};
353+ //final int width = 10,
354+ // height = 10;
355+ //final long saturation_sum = 21367;
356+
357+ // Full image 507x446 pixels
358+ final int [] leaf_pix ;
359+ final int width = 507 ,
360+ height = 446 ;
361+ final long saturation_sum = 23641758 ; // Sum of saturation values * 255.0, each cast to int.
362+
363+ try {
364+ final URL url = new URL ( "https://imagej.nih.gov/ij/images/leaf.jpg" );
365+ //final URL url = new URL( "http://127.0.0.1/images/leaf.jpg" );
366+ final ReadableByteChannel rbc = Channels .newChannel ( url .openStream () );
367+ final byte [] bytes = new byte [ 36642 ];
368+ final ByteBuffer bb = ByteBuffer .wrap ( bytes );
369+ rbc .read ( bb );
370+
371+ final BufferedImage bi = ImageIO .read ( new ByteArrayInputStream ( bytes ) );
372+ leaf_pix = new int [width * height ];
373+ final PixelGrabber pg = new PixelGrabber ( bi , 0 , 0 , width , height , leaf_pix , 0 , width );
374+ try {
375+ pg .grabPixels ();
376+ } catch (InterruptedException e ){};
377+
378+ } catch (Exception e ) {
379+ e .printStackTrace ();
380+ return false ;
381+ }
382+
383+ final long [] dims = new long []{ width , height };
329384
330385 // Fails, don't know why
331- //final ArrayImg< ARGBType, IntArray > rgb = new ArrayImg< ARGBType, IntArray >( new IntArray( leaf_crop_pix ), dims, new Fraction() );
386+ //final ArrayImg< ARGBType, IntArray > rgb = new ArrayImg< ARGBType, IntArray >( new IntArray( leaf_pix ), dims, new Fraction() );
332387
333388 final ArrayImg < ARGBType , ? > rgb = new ArrayImgFactory < ARGBType >( new ARGBType () ).create ( dims );
334389 int k = 0 ;
335390 for ( final ARGBType t : rgb )
336391 {
337- t .set ( leaf_crop_pix [ k ++ ] );
392+ t .set ( leaf_pix [ k ++ ] );
338393 }
339394
340395 final IterableInterval < UnsignedByteType >
@@ -347,77 +402,93 @@ protected boolean testSaturationPerformance( final int n_iterations, final boole
347402 long minLM = Long .MAX_VALUE ,
348403 maxLM = 0 ;
349404 double meanLM = 0 ;
405+
406+ // Compute saturation
407+ final Compute op1 =
408+ compute ( let ( "red" , red ,
409+ "green" , green ,
410+ "blue" , blue ,
411+ "max" , max ( var ( "red" ), var ( "green" ), var ( "blue" ) ),
412+ "min" , min ( var ( "red" ), var ( "green" ), var ( "blue" ) ),
413+ IF ( EQ ( 0 , var ( "max" ) ),
414+ 0 ,
415+ mul ( div ( sub ( var ( "max" ), var ( "min" ) ),
416+ var ( "max" ) ),
417+ 255.0f ) ) ) );
350418
351- for ( int i =0 ; i < n_iterations ; ++i ) {
419+ for ( int i =0 ; i < n_iterations ; ++i )
420+ {
352421 final long t0 = System .nanoTime ();
353-
354- // Compute saturation
355- compute ( let ( "red" , red ,
356- "green" , green ,
357- "blue" , blue ,
358- "max" , max ( var ( "red" ), var ( "green" ), var ( "blue" ) ),
359- "min" , min ( var ( "red" ), var ( "green" ), var ( "blue" ) ),
360- IF ( EQ ( 0 , var ( "max" ) ),
361- 0 ,
362- div ( sub ( var ( "max" ), var ( "min" ) ),
363- var ( "max" ) ) ) ) )
364- .into ( saturation );
422+
423+ op1 .into ( saturation );
365424
366425 final long t1 = System .nanoTime ();
367426 minLM = Math .min (minLM , t1 - t0 );
368427 maxLM = Math .max (maxLM , t1 - t0 );
369428 meanLM += (t1 - t0 ) / (double )(n_iterations );
370429 }
430+
431+ assertTrue ( "op1" , sumAsInts ( saturation , 1.0 ) == saturation_sum );
371432
372433 if ( print ) System .out .println ("ImgMath 1 saturation performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
373434
374435 // Reset
375436 minLM = Long .MAX_VALUE ;
376437 maxLM = 0 ;
377438 meanLM = 0 ;
439+
440+ // Compute saturation: very slightly slower than with vars (unsurprisingly), but easier to read
441+ final Compute op2 =
442+ compute ( let ( "max" , max ( red , green , blue ),
443+ "min" , min ( red , green , blue ),
444+ IF ( EQ ( 0 , var ( "max" ) ),
445+ 0 ,
446+ div ( sub ( var ( "max" ), var ( "min" ) ),
447+ var ( "max" ) ) ) ) );
378448
379449 for ( int i =0 ; i < n_iterations ; ++i ) {
380450 final long t0 = System .nanoTime ();
381451
382- // Compute saturation: very slightly slower than with vars (unsurprisingly), but easier to read
383- compute ( let ( "max" , max ( red , green , blue ),
384- "min" , min ( red , green , blue ),
385- IF ( EQ ( 0 , var ( "max" ) ),
386- 0 ,
387- div ( sub ( var ( "max" ), var ( "min" ) ),
388- var ( "max" ) ) ) ) )
389- .into ( saturation );
452+ op2 .into ( saturation );
390453
391454 final long t1 = System .nanoTime ();
392455 minLM = Math .min (minLM , t1 - t0 );
393456 maxLM = Math .max (maxLM , t1 - t0 );
394457 meanLM += (t1 - t0 ) / (double )(n_iterations );
395458 }
459+
460+ if ( print ) System .out .println ( "Sum: " + sumAsInts ( saturation , 255.0 ) + ", saturation_sum: " + saturation_sum );
461+
462+ assertTrue ( "op2" , sumAsInts ( saturation , 255.0 ) == saturation_sum );
396463
397464 if ( print ) System .out .println ("ImgMath 2 saturation performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
398465
399466 // Reset
400467 minLM = Long .MAX_VALUE ;
401468 maxLM = 0 ;
402469 meanLM = 0 ;
403-
470+
471+ // Compute saturation: easier to read without vars
472+ final Compute op3 =
473+ compute ( let ( "max" , max ( red , green , blue ),
474+ "min" , min ( red , green , blue ),
475+ IF ( EQ ( 0 , var ( "max" ) ),
476+ THEN ( 0 ),
477+ ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
478+ var ( "max" ) ) ) ) ) );
479+
404480 for ( int i =0 ; i < n_iterations ; ++i ) {
405481 final long t0 = System .nanoTime ();
406-
407- // Compute saturation: easier to read without vars
408- compute ( let ( "max" , max ( red , green , blue ),
409- "min" , min ( red , green , blue ),
410- IF ( EQ ( 0 , var ( "max" ) ),
411- THEN ( 0 ),
412- ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
413- var ( "max" ) ) ) ) ) )
414- .into ( saturation );
482+
483+ op3 .into ( saturation );
415484
416485 final long t1 = System .nanoTime ();
417486 minLM = Math .min (minLM , t1 - t0 );
418487 maxLM = Math .max (maxLM , t1 - t0 );
419488 meanLM += (t1 - t0 ) / (double )(n_iterations );
420489 }
490+
491+ assertTrue ( "op3" , sumAsInts ( saturation , 255.0 ) == saturation_sum );
421492
422493 if ( print ) System .out .println ("ImgMath 3 saturation performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
423494
@@ -446,52 +517,60 @@ protected boolean testSaturationPerformance( final int n_iterations, final boole
446517 cb .next ().setReal ( b .next ().getRealDouble () );
447518 }
448519
520+ // Compute saturation:
521+ final Compute op4 =
522+ compute ( let ( "max" , max ( redF , greenF , blueF ),
523+ "min" , min ( redF , greenF , blueF ),
524+ IF ( EQ ( 0 , var ( "max" ) ),
525+ THEN ( 0 ),
526+ ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
527+ var ( "max" ) ) ) ) ) );
528+
449529 for ( int i =0 ; i < n_iterations ; ++i ) {
450530 final long t0 = System .nanoTime ();
451531
452- // Compute saturation:
453- compute ( let ( "max" , max ( redF , greenF , blueF ),
454- "min" , min ( redF , greenF , blueF ),
455- IF ( EQ ( 0 , var ( "max" ) ),
456- THEN ( 0 ),
457- ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
458- var ( "max" ) ) ) ) ) )
459- .into ( saturation );
532+ op4 .into ( saturation );
460533
461534 final long t1 = System .nanoTime ();
462535 minLM = Math .min (minLM , t1 - t0 );
463536 maxLM = Math .max (maxLM , t1 - t0 );
464537 meanLM += (t1 - t0 ) / (double )(n_iterations );
465538 }
466539
540+ assertTrue ( "op4" , sumAsInts ( saturation , 255.0 ) == saturation_sum );
541+
467542 if ( print ) System .out .println ("ImgMath 4 saturation without vars performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
468543
469544 // Reset
470545 minLM = Long .MAX_VALUE ;
471546 maxLM = 0 ;
472547 meanLM = 0 ;
548+
549+ // Compute saturation:
550+ final Compute op5 =
551+ compute ( let ( "red" , redF ,
552+ "green" , greenF ,
553+ "blue" , blueF ,
554+ "max" , max ( var ("red" ), var ("green" ), var ("blue" ) ),
555+ "min" , min ( var ("red" ), var ("green" ), var ("blue" ) ),
556+ IF ( EQ ( 0 , var ( "max" ) ),
557+ THEN ( 0 ),
558+ ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
559+ var ( "max" ) ) ) ) ) );
473560
474561 for ( int i =0 ; i < n_iterations ; ++i ) {
475562 final long t0 = System .nanoTime ();
476563
477- // Compute saturation:
478- compute ( let ( "red" , redF ,
479- "green" , greenF ,
480- "blue" , blueF ,
481- "max" , max ( var ("red" ), var ("green" ), var ("blue" ) ),
482- "min" , min ( var ("red" ), var ("green" ), var ("blue" ) ),
483- IF ( EQ ( 0 , var ( "max" ) ),
484- THEN ( 0 ),
485- ELSE ( div ( sub ( var ( "max" ), var ( "min" ) ),
486- var ( "max" ) ) ) ) ) )
487- .into ( saturation );
564+ op5 .into ( saturation );
488565
489566 final long t1 = System .nanoTime ();
490567 minLM = Math .min (minLM , t1 - t0 );
491568 maxLM = Math .max (maxLM , t1 - t0 );
492569 meanLM += (t1 - t0 ) / (double )(n_iterations );
493570 }
494571
572+ assertTrue ( "op5" , sumAsInts ( saturation , 255.0 ) == saturation_sum );
573+
495574 if ( print ) System .out .println ("ImgMath 5 saturation with vars performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
496575
497576 }
@@ -512,11 +591,11 @@ protected boolean testSaturationPerformance( final int n_iterations, final boole
512591 final Cursor < FloatType > cs = saturation .cursor ();
513592
514593 while (cs .hasNext ()) {
515- final UnsignedByteType r = cr .next (),
516- g = cg .next (),
517- b = cb .next ();
518- final float max = Math .max ( r . get () , Math .max ( g . get () , b . get () ) ),
519- min = Math .min ( r . get () , Math .min ( g . get () , b . get () ) );
594+ final int r = cr .next (). getInteger (),
595+ g = cg .next (). getInteger (),
596+ b = cb .next (). getInteger ();
597+ final float max = Math .max ( r , Math .max ( g , b ) ),
598+ min = Math .min ( r , Math .min ( g , b ) );
520599 cs .next ().set ( 0.0f == max ? 0.0f : (max - min ) / max );
521600 }
522601
@@ -525,6 +604,8 @@ protected boolean testSaturationPerformance( final int n_iterations, final boole
525604 maxLM = Math .max (maxLM , t1 - t0 );
526605 meanLM += (t1 - t0 ) / (double )(n_iterations );
527606 }
607+
608+ assertTrue ( "low-level" , sumAsInts ( saturation , 255.0 ) == saturation_sum );
528609
529610 if ( print ) System .out .println ("Low-level saturation performance: min: " + (minLM / 1000.0 ) + " ms, max: " + (maxLM / 1000.0 ) + " ms, mean: " + (meanLM / 1000.0 ) + " ms" );
530611
@@ -626,8 +707,8 @@ public void test1IfThenElse() {
626707
627708 //@Test
628709 public void test1IfThenElsePerformance () {
629- assertTrue ( testSaturationPerformance ( 200 , false ) ); // warm-up
630- assertTrue ( testSaturationPerformance ( 200 , true ) );
710+ // assertTrue ( testSaturationPerformance( 200, false ) ); // warm-up
711+ assertTrue ( testSaturationPerformance ( 30 , true ) );
631712 }
632713
633714 @ Test
0 commit comments