From 36761bcfd7594a573e83a74f3f68460972c82360 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 24 Jan 2017 09:36:25 +0000 Subject: [PATCH] make vips_gaussnoise() pixels reproducible previously, pixel values were regenerated on every calculation, so they changed on recomputation pixel values are now generated from the pixel (x, y) coordinate plus a per-call seed thanks MvGulik, see https://github.com/jcupitt/nip2/issues/60 https://github.com/jcupitt/libvips/issues/583 --- ChangeLog | 1 + libvips/create/gaussnoise.c | 23 +++++++++++++++++++++-- libvips/create/perlin.c | 20 ++------------------ libvips/create/worley.c | 24 ++++-------------------- libvips/include/vips/util.h | 3 +++ libvips/iofuncs/util.c | 17 +++++++++++++++++ 6 files changed, 48 insertions(+), 40 deletions(-) diff --git a/ChangeLog b/ChangeLog index 69e89bbc..74753a90 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,7 @@ - kick load operations from cache on read error, thanks gaillard - fix return from C++ assignment operator overloads (+=, -= etc) - add @max_slope to vips_local_hist() to implement CLAHE, thanks hunter-87 +- vips_gaussnoise() pixels are reproducible on recalc, thanks MvGulik 8/12/16 started 8.4.5 - allow libgsf-1.14.26 to help centos, thanks tdiprima diff --git a/libvips/create/gaussnoise.c b/libvips/create/gaussnoise.c index 1530905e..d8a2e7f4 100644 --- a/libvips/create/gaussnoise.c +++ b/libvips/create/gaussnoise.c @@ -22,6 +22,10 @@ * - redo as a class * 8/11/14 * - use g_random_double() + * 24/1/17 + * - use g_random_double() once per image, use vips__random() for pixel + * values from (x, y) position ... makes pixels reproducible on + * recalculation */ /* @@ -71,6 +75,9 @@ typedef struct _VipsGaussnoise { double mean; double sigma; + /* Per-image seed. + */ + guint32 seed; } VipsGaussnoise; typedef VipsCreateClass VipsGaussnoiseClass; @@ -93,12 +100,19 @@ vips_gaussnoise_gen( VipsRegion *or, void *seq, void *a, void *b, int x; for( x = 0; x < sz; x++ ) { + guint32 seed; double sum; int i; + seed = gaussnoise->seed; + seed = vips__random_add( seed, or->valid.left + x ); + seed = vips__random_add( seed, or->valid.top + y ); + sum = 0.0; - for( i = 0; i < 12; i++ ) - sum += g_random_double(); + for( i = 0; i < 12; i++ ) { + seed = vips__random( seed ); + sum += (double) seed / UINT_MAX; + } q[x] = (sum - 6.0) * gaussnoise->sigma + gaussnoise->mean; @@ -124,6 +138,11 @@ vips_gaussnoise_build( VipsObject *object ) vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); + /* The seed for this image. Each pixel is seeded by this plus the (x, + * y) coordinate. + */ + gaussnoise->seed = UINT_MAX * g_random_double(); + if( vips_image_generate( create->out, NULL, vips_gaussnoise_gen, NULL, gaussnoise, NULL ) ) return( -1 ); diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c index 7d535794..c727347a 100644 --- a/libvips/create/perlin.c +++ b/libvips/create/perlin.c @@ -92,21 +92,6 @@ typedef struct _Sequence { } Sequence; -/* A very simple random number generator. See: - * http://isthe.com/chongo/tech/comp/fnv/#FNV-source - */ -static guint32 -vips_perlin_random( guint32 seed ) -{ - return( 1103515245u * seed + 12345 ); -} - -static guint32 -vips_perlin_seed_add( guint32 seed, int value ) -{ - return( ((2166136261u ^ seed) * 16777619u) ^ value ); -} - /* Generate a 3 x 3 grid of cells around a point. */ static void @@ -135,13 +120,12 @@ vips_perlin_create_cells( VipsPerlin *perlin, if( cy >= perlin->cells_down ) cy = 0; - seed = vips_perlin_seed_add( seed, cy ); + seed = vips__random_add( seed, cy ); if( cx >= perlin->cells_across ) cx = 0; - seed = vips_perlin_seed_add( seed, cx ); + seed = vips__random_add( seed, cx ); - seed = vips_perlin_random( seed ); angle = (seed ^ (seed >> 8) ^ (seed >> 16)) & 0xff; gx[ci] = vips_perlin_cos[angle]; diff --git a/libvips/create/worley.c b/libvips/create/worley.c index bdd30fbd..0f057fba 100644 --- a/libvips/create/worley.c +++ b/libvips/create/worley.c @@ -107,21 +107,6 @@ typedef struct _Sequence { } Sequence; -/* A very simple random number generator. See: - * http://isthe.com/chongo/tech/comp/fnv/#FNV-source - */ -static guint32 -vips_worley_random( guint32 seed ) -{ - return( 1103515245u * seed + 12345 ); -} - -static guint32 -vips_worley_seed_add( guint32 seed, int value ) -{ - return( ((2166136261u ^ seed) * 16777619u) ^ value ); -} - /* Generate a 3 x 3 grid of cells around a point. */ static void @@ -154,7 +139,7 @@ vips_worley_create_cells( VipsWorley *worley, value = worley->cells_across - 1; else value = cell->cell_x; - seed = vips_worley_seed_add( seed, value ); + seed = vips__random_add( seed, value ); if( cell->cell_y >= worley->cells_down ) value = 0; @@ -162,20 +147,19 @@ vips_worley_create_cells( VipsWorley *worley, value = worley->cells_down - 1; else value = cell->cell_y; - seed = vips_worley_seed_add( seed, value ); + seed = vips__random_add( seed, value ); /* [1, MAX_FEATURES) */ - seed = vips_worley_random( seed ); cell->n_features = (seed % (MAX_FEATURES - 1)) + 1; for( j = 0; j < cell->n_features; j++ ) { - seed = vips_worley_random( seed ); + seed = vips__random( seed ); cell->feature_x[j] = cell->cell_x * worley->cell_size + seed % worley->cell_size; - seed = vips_worley_random( seed ); + seed = vips__random( seed ); cell->feature_y[j] = cell->cell_y * worley->cell_size + seed % worley->cell_size; diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index 027acb11..5ac2c450 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -328,6 +328,9 @@ void vips__change_suffix( const char *name, char *out, int mx, char *vips_realpath( const char *path ); +guint32 vips__random( guint32 seed ); +guint32 vips__random_add( guint32 seed, int value ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 3842dbf7..ceea8d3f 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -1912,3 +1912,20 @@ vips_realpath( const char *path ) return( real ); } + +/* A very simple random number generator. See: + * http://isthe.com/chongo/tech/comp/fnv/#FNV-source + */ +guint32 +vips__random( guint32 seed ) +{ + return( 1103515245u * seed + 12345 ); +} + +guint32 +vips__random_add( guint32 seed, int value ) +{ + seed = ((2166136261u ^ seed) * 16777619u) ^ value; + + return( vips__random( seed ) ); +}