The Python Oracle

Why does NumPy's random function seemingly display a pattern in its generated values?

--------------------------------------------------
Hire the world's top talent on demand or became one of them at Toptal: https://topt.al/25cXVn
and get $2,000 discount on your first invoice
--------------------------------------------------

Take control of your privacy with Proton's trusted, Swiss-based, secure services.
Choose what you need and safeguard your digital life:
Mail: https://go.getproton.me/SH1CU
VPN: https://go.getproton.me/SH1DI
Password Manager: https://go.getproton.me/SH1DJ
Drive: https://go.getproton.me/SH1CT


Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzle Game 2 Looping

--

Chapters
00:00 Why Does Numpy'S Random Function Seemingly Display A Pattern In Its Generated Values?
01:20 Accepted Answer Score 17
02:09 Answer 2 Score 3
02:52 Answer 3 Score 1
04:07 Thank you

--

Full question
https://stackoverflow.com/questions/5029...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #numpy #random #pythonimaginglibrary

#avk47



ACCEPTED ANSWER

Score 17


Don't blame Numpy, blame PIL / Pillow. ;) You're generating floats, but PIL expects integers, and its float to int conversion is not doing what we want. Further research is required to determine exactly what PIL is doing...

In the mean time, you can get rid of those lines by explicitly converting your values to unsigned 8 bit integers:

img_arrays = (np.random.random((100, 256, 256, 3)) * 255).astype(np.uint8)

As FHTMitchell notes in the comments, a more efficient form is

img_arrays = np.random.randint(0, 256, (100, 256, 256, 3), dtype=np.uint8) 

Here's typical output from that modified code:

random image made using Numpy


The PIL Image.fromarray function has a known bug, as described here. The behaviour you're seeing is probably related to that bug, but I guess it could be an independent one. ;)

FWIW, here are some tests and workarounds I did on the bug mentioned on the linked question.




ANSWER 2

Score 3


I'm pretty sure the problem is to do with the dtype, but not for the reasons you think. Here is one with np.random.randint(0, 256, (1, 256, 256, 3), dtype=np.uint32) note the dtype is not np.uint8:

enter image description here

Can you see the pattern ;)? PIL interprets 32 bit (4 byte) values (probably as 4 pixels RGBK) differently from 8 bit values (RGB for one pixel). (See PM 2Ring's answer).

Originally you were passing 64 bit float values, these are going to also are interpreted differently (and probably incorrectly from how you intended).




ANSWER 3

Score 1


As others have noted, these patterns have nothing to do with NumPy's random number generation; the problem is simply that PIL's 'RGB' mode expects to get an array of dtype uint8, and when given something else, tries to interpret the raw bytes as if they were an array of uint8s. Here, you are passing 8-byte float64s (NumPy's default when you don't specify a dtype) and this produces the result you see.

You're expecting every random number from 0-255 in your array to define the value for one color channel of one pixel, but in reality, it's defining the value for 8 successive color channels. For instance, the very first random number - which you intend to be the value of the "red" channel of the top-left pixel - is in fact defining the red, green, and blue channels of the top-left pixel and the one to the right of that and the red and green channels of the pixel to the right of that. Oops.

The simplest test that shows that these patterns are not in fact emerging from NumPy's RNG is to simply set all the values in the array to 255 instead of random numbers, and then display that:

>>> import numpy as np
>>> from PIL import Image
>>> img_array = np.full((256, 256, 3), 255.0)
>>> print(img_array.dtype)
float64
>>> Image.fromarray(img_array, 'RGB').show()

Image output by the above code

Sure enough, we still see a pattern of vertical lines.