Randomness Of Python's Random
I'm using Python to generate images using dashed lines for stippling. The period of the dashing is constant, what changes is dash/space ratio. This produces something like this:
However in that image the dashing has a uniform origin and this creates unsightly vertical gutters. So I tried to randomize the origin to remove the gutters. This sort of works but there is an obvious pattern:
Wondering where this comes from I made a very simple test case with stacked dashed straight lines:
- dash ratio: 50%
- dash period 20px
- origin shift from -10px to +10px using
random.uniform(-10.,+10.)(*) (after an initial
And with added randomness:
So there is still pattern. What I don't understand is that to get a visible gutter you need to have 6 or 7 consecutive values falling in the same range (says, half the total range), which should be a 1/64 probability but seems to happen a lot more often in the 200 lines generated.
Am I misunderstanding something? Is it just our human brain which is seeing patterns where there is none? Could there be a better way to generate something more "visually random" (python 2.7, and preferably without installing anything)?
(*) partial pixels are valid in that context
Annex: the code I use (this is a Gimp script):
#!/usr/bin/env python # -*- coding: iso-8859-15 -*- # Python script for Gimp (requires Gimp 2.10) # Run on a 400x400 image to see something without having to wait too much # Menu entry is in "Test" submenu of image menubar import random,traceback from gimpfu import * def constant(minShift,maxShift): return 0 def triangle(minShift,maxShift): return random.triangular(minShift,maxShift) def uniform(minShift,maxShift): return random.uniform(minShift,maxShift) def gauss(minShift,maxShift): return random.gauss((minShift+maxShift)/2,(maxShift-minShift)/2) variants=[('Constant',constant),('Triangle',triangle),('Uniform',uniform),('Gauss',gauss)] def generate(image,name,generator): random.seed() layer=gimp.Layer(image, name, image.width, image.height, RGB_IMAGE,100, LAYER_MODE_NORMAL) image.add_layer(layer,0) layer.fill(FILL_WHITE) path=pdb.gimp_vectors_new(image,name) # Generate path, horizontal lines are 2px apart, # Start on left has a random offset, end is on the right edge right edge for i in range(1,image.height, 2): shift=generator(-10.,10.) points=[shift,i]*3+[image.width,i]*3 pdb.gimp_vectors_stroke_new_from_points(path,0, len(points),points,False) pdb.gimp_image_add_vectors(image, path, 0) # Stroke the path pdb.gimp_context_set_foreground(gimpcolor.RGB(0, 0, 0, 255)) pdb.gimp_context_set_stroke_method(STROKE_LINE) pdb.gimp_context_set_line_cap_style(0) pdb.gimp_context_set_line_join_style(0) pdb.gimp_context_set_line_miter_limit(0.) pdb.gimp_context_set_line_width(2) pdb.gimp_context_set_line_dash_pattern(2,[5,5]) pdb.gimp_drawable_edit_stroke_item(layer,path) def randomTest(image): image.undo_group_start() gimp.context_push() try: for name,generator in variants: generate(image,name,generator) except Exception as e: print e.args pdb.gimp_message(e.args) traceback.print_exc() gimp.context_pop() image.undo_group_end() return; ### Registration desc="Python random test" register( "randomize-test",desc,'','','','',desc,"*", [(PF_IMAGE, "image", "Input image", None),],, randomTest,menu="<Image>/Test", ) main()
Think of it like this: a gutter is perceptible until it is obstructed (or almost so). This only happens when two successive lines are almost completely out of phase (with the black segments in the first line lying nearly above the white segments in the next). Such extreme situations only happens about one out of every 10 rows, hence the visible gutters which seem to extend around 10 rows before being obstructed.
Looked at another way -- if you print out the image, there really are longish white channels through which you can easily draw a line with a pen. Why should your mind not perceive them?
To get better visual randomness, find a way to make successive lines dependent rather than independent in such a way that the almost-out-of-phase behavior appears more often.
- → What are the pluses/minuses of different ways to configure GPIOs on the Beaglebone Black?
- → Django, code inside <script> tag doesn't work in a template
- → React - Django webpack config with dynamic 'output'
- → GAE Python app - Does URL matter for SEO?
- → Put a Rendered Django Template in Json along with some other items
- → session disappears when request is sent from fetch
- → Python Shopify API output formatted datetime string in django template
- → Shopify app: adding a new shipping address via webhook
- → Shopify + Python library: how to create new shipping address
- → shopify python api: how do add new assets to published theme?
- → Access 'HTTP_X_SHOPIFY_SHOP_API_CALL_LIMIT' with Python Shopify Module