randrange - Aleatoriedad de Python al azar
python random uniform (4)
Estoy usando Python para generar imágenes usando líneas discontinuas para puntear. El período del guión es constante, lo que cambia es la relación guión / espacio. Esto produce algo como esto:
Sin embargo, en esa imagen, el trazado tiene un origen uniforme y esto crea canales desagradables verticales. Así que traté de aleatorizar el origen para eliminar las canaletas. Este tipo de obras, pero hay un patrón obvio:
Preguntándome de dónde viene esto, hice un caso de prueba muy simple con líneas rectas apiladas apiladas:
- cociente del tablero: 50%
- período de guión 20px
-
cambio de origen de -10px a + 10px usando
random.uniform(-10.,+10.)
(*) (después de una inicialrandom.seed()
Y con mayor aleatoriedad:
Así que todavía hay patrón. Lo que no entiendo es que para obtener un canal visible es necesario tener 6 o 7 valores consecutivos que caigan en el mismo rango (es decir, la mitad del rango total), que debería tener una probabilidad de 1/64 pero parece que sucede mucho Más a menudo en las 200 líneas generadas.
¿Estoy malinterpretando algo? ¿Es solo nuestro cerebro humano el que está viendo patrones donde no hay ninguno? ¿Podría haber una mejor manera de generar algo más "visualmente aleatorio" (python 2.7, y preferiblemente sin instalar nada)?
(*) los píxeles parciales son válidos en ese contexto
Anexo: el código que uso (este es un script de Gimp):
#!/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[0]
pdb.gimp_message(e.args[0])
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()
Esto es ligeramente contraintuitivo, pero a medida que agrega elementos aleatorios juntos, la aleatoriedad se reduce. Si sigo correctamente el rango de cada elemento es 10px - 30px. Así que el tamaño total de 10 elementos es de 100px a 300px, pero la distribución no es uniforme en ese rango. Los extremos son muy poco probables y, en promedio, estarán muy cerca de 200px, por lo que surgirá el patrón fundamental de 20px. Tu distribución aleatoria necesita evitar esto.
EDIT: Veo que entendí un poco mal, y todos los guiones son 20px con un desplazamiento aleatorio. Por lo tanto, creo que mirar cualquier conjunto vertical de guiones parecería aleatorio, pero ese mismo conjunto aleatorio se repite en la página, dando el patrón.
Hay al menos una razón obvia por la que vemos un patrón en la imagen "aleatoria": los 400x400 píxeles son exactamente los mismos 20x400 píxeles repetidos 20 veces.
Entonces, cada movimiento aparente se repite 20 veces en paralelo, lo que realmente ayuda al cerebro a analizar la imagen.
En realidad, el mismo patrón de 10 píxeles de ancho se repite 40 veces, alternando entre blanco y negro:
Puede aleatorizar el período de guión por separado para cada línea (por ejemplo, entre 12 y 28):
Aquí está el código correspondiente:
import numpy as np
import random
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams[''figure.figsize''] = [13, 13]
N = 400
def random_pixels(width, height):
return np.random.rand(height, width) < 0.5
def display(table):
plt.imshow(table, cmap=''Greys'', interpolation=''none'')
plt.show()
display(random_pixels(N, N))
def stripes(width, height, stripe_width):
table = np.zeros((height, width))
cycles = width // (stripe_width * 2) + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
for i in range(height):
table[i] = np.tile(pattern, cycles)[:width]
return table
display(stripes(N, N, 10))
def shifted_stripes(width, height, stripe_width):
table = np.zeros((height, width))
period = stripe_width * 2
cycles = width // period + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
for i in range(height):
table[i] = np.roll(np.tile(pattern, cycles), random.randrange(0, period))[:width]
return table
display(shifted_stripes(N, N, 10))
def flexible_stripes(width, height, average_width, delta):
table = np.zeros((height, width))
for i in range(height):
stripe_width = random.randint(average_width - delta, average_width + delta)
period = stripe_width * 2
cycles = width // period + 1
pattern = np.concatenate([np.zeros(stripe_width), np.ones(stripe_width)])
table[i] = np.roll(np.tile(pattern, cycles), random.randrange(0, period))[:width]
return table
display(flexible_stripes(N, N, 10, 4))
Piénselo así: un canal es perceptible hasta que se obstruye (o casi). Esto solo ocurre cuando dos líneas sucesivas están casi completamente desfasadas (con los segmentos negros en la primera línea casi por encima de los segmentos blancos en la siguiente). Tales situaciones extremas solo ocurren aproximadamente una de cada 10 filas, por lo tanto, los canales visibles que parecen extenderse alrededor de 10 filas antes de ser obstruidos.
Mirado de otra manera: si imprime la imagen, hay canales blancos muy largos a través de los cuales puede dibujar fácilmente una línea con un bolígrafo. ¿Por qué tu mente no las percibe?
Para obtener una mejor aleatoriedad visual, encuentre una manera de hacer que las líneas sucesivas sean más dependientes que independientes, de tal manera que el comportamiento casi fuera de fase aparezca con mayor frecuencia.
Publicar mi solución final como una respuesta, pero por favor, vota a otros.
John Coleman tiene un punto cuando dice:
Para obtener una mejor aleatoriedad visual, encuentre una manera de hacer que las líneas sucesivas sean más dependientes que independientes, de tal manera que el comportamiento casi fuera de fase aparezca con mayor frecuencia.
Entonces, finalmente, la mejor manera de evitar las canaletas es evitar la aleatoriedad y tener un esquema de turnos muy fijo, y uno que funciona bien es un ciclo de 4 fases 0,25%, 75%, 50%:
Está bien, todavía hay un patrón de diamante ligero, pero es mucho menos visible que los patrones introducidos por los esquemas aleatorios que probé.