Ξένη Γήινος
Ξένη Γήινος

Reputation: 3072

How do I create a smooth image containing all RGB colors in NumPy?

I want to create an uncompressed image containing every RGB color exactly once in NumPy.

I am using 24-bit color depth here. An RGB color contains three channels: Red, Green and Blue, each channel can have 256 intensities, so an RGB color is a sequence of 3 bytes and the total number of RGB colors is (2^8)^3 = 2^24 = 16777216.

An image containing every RGB color exactly once would therefore have 16777216 pixels. The square root of 16777216 is 2^12 = 4096, thus I want an image of resolution 4096x4096.

An uncompressed image of such would be exactly have a size of 16777216*3/1048576=48MiB. Because the image uploading size limit is 2MiB I can only upload compressed versions.

Here is what I have got so far:

enter image description here

It isn't smooth because there are horizontal bands with height of 16 pixels.

My code:

import cv2
import numpy as np

byte = range(256)
colors = np.array(np.meshgrid(byte, byte, byte)).T.reshape(-1,3)
striped = colors.reshape((4096, 4096, 3))
img = np.zeros((4096, 4096, 3), dtype=np.uint8)
for i in range(4096):
    img[i] = cv2.rotate(striped[i].reshape((16, 256, 3)), cv2.ROTATE_90_CLOCKWISE).reshape((-1, 3))

new_img = np.zeros((4096, 4096, 3), dtype=np.uint8)
rot90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
for i in range(4096):
    new_img[i] = cv2.rotate(rot90[i].reshape((256, 16, 3)), cv2.ROTATE_90_CLOCKWISE).reshape((-1, 3))
new_img = np.rot90(new_img)
cv2.imwrite('D:/BGR_colors.png', striped, (cv2.IMWRITE_PNG_COMPRESSION, 0))
cv2.imwrite('D:/BGR_colors_new.png', img, (cv2.IMWRITE_PNG_COMPRESSION, 0))
cv2.imwrite('D:/BGR_colors_new1.png', new_img, (cv2.IMWRITE_PNG_COMPRESSION, 0))
cv2.imwrite('D:/BGR_colors_compressed.png', striped, (cv2.IMWRITE_PNG_COMPRESSION, 9))
cv2.imwrite('D:/BGR_colors_new_compressed.png', img, (cv2.IMWRITE_PNG_COMPRESSION, 9))
cv2.imwrite('D:/BGR_colors_new1_compressed.png', new_img, (cv2.IMWRITE_PNG_COMPRESSION, 9))

So I first generated all RGB colors using Cartesian product and reshaped it to 4096x4096 image.

The result:

enter image description here

It wasn't smooth, it has many small bands, there are 16 segments of 256 pixels horizontally that look very similar, and vertically there are 256 bands of 16 pixels.

There are 4096 rectangles of size 256x16. I want to make the image smooth, by eliminating the rectangles.

I want to get rid of the rectangles, by taking the first pixel of each of the 16 groups of 256 pixels in each row, put them together, then take the second pixel of each group, and so on. Similarly, this should happen to the 256 groups of 16 pixels in each column.

I only succeeded in eliminating the horizontal bands, I have tried many methods to eliminate the vertical bands but the result only gets worse:

enter image description here

How can I get rid of the vertical bands, and how can I get the final result in the least amount of steps and only using NumPy methods?

Upvotes: 1

Views: 1096

Answers (1)

Ξένη Γήινος
Ξένη Γήινος

Reputation: 3072

Turns out I can get the same result as my partially intended image, just by sorting it. I only need to sort the 2D array column-wise by specifying axis=1 in np.sort function call. This will make the array be sorted horizontally.

import cv2
import numpy as np

byte = range(256)
colors = np.array(np.meshgrid(byte, byte, byte), dtype=np.uint8).T.reshape(-1,3)
striped = colors.reshape((4096, 4096, 3))
img = np.sort(striped, axis=1)

img

enter image description here

But unfortunately I now believe what I originally intended to be impossible.

I tried multiple methods without success. I tried very hard and I cannot find a solution.

Sorting the original image row-wise:

np.sort(striped, axis=0)

enter image description here

Sort the image twice (first column-wise then row-wise)

np.sort(img, axis=0)

enter image description here

The result seems to only contain shades of grey, magenta and green, yet it actually contains shades of blue and yellow, it really contains all colors, but somehow only two tunes are prominent. I cannot see blue in it, so my color perception prevents me from perceiving such image.


Update

I have managed to smooth the image by resizing it and applying Gaussian blur:

smoothed = cv2.resize(img, (1024, 1024), interpolation=cv2.INTER_CUBIC)
smoothed = cv2.GaussianBlur(smoothed, (15, 15), cv2.BORDER_DEFAULT)

enter image description here

It is like what I originally wanted, except it obviously doesn't contain all RGB colors.

Upvotes: 0

Related Questions