Generate 8 bit image with numpy - python-3.x

I'm trying to generate an image of all 8 bit colours. And this is the important bit: 1 pixel represents 1 unique colour. That's 2^8 or 256 colours - should be a 32 x 32 image.
The plan is to be able to change the bit depth and create a different image. ie 65536 colours for 16 bit.
Here's what I have:
import numpy as np
from PIL import Image
# --------------------------------------------------------------
def create_image(output, width, height, pixels):
# Convert the pixels into an array using numpy
array = np.array(pixels, dtype=np.uint8)
img = Image.fromarray(array)
img.save(output)
# --------------------------------------------------------------
bit = 8
cmap = plt.get_cmap("viridis", 2**bit)
a = cmap(np.linspace(0,1,2**bit))
numOfCols = (len(a)) # number of cols
x = int(np.sqrt(2**bit)*2)
y = int(np.sqrt(2**bit)*2)
arr = np.reshape(a, (x, y))
create_image("test.png", x, y, arr)
I'm new to numpy and I may have the initial size of the array wrong, as I get an error
ValueError: cannot reshape array of size 1024 into shape (16,16)
if I try to force it into an array that's 16 x 16.
Secondly, the image is just black, which is great for coffee, not so good for my results.
How do I transfer the array with all the colour data to the image properly?

First of all, your colormap generates an array of values in the following fashion:
In [71]: mymap = cmap(np.linspace(0, 1, 2 ** bit))
In [72]: mymap
Out[72]:
array([[0.267004, 0.004874, 0.329415, 1. ],
[0.26851 , 0.009605, 0.335427, 1. ],
[0.269944, 0.014625, 0.341379, 1. ],
...,
[0.974417, 0.90359 , 0.130215, 1. ],
[0.983868, 0.904867, 0.136897, 1. ],
[0.993248, 0.906157, 0.143936, 1. ]])
In this question, it's noted that PIL cannot handle the 32-bit floating point RGB format.
It does support tuples of 3 8-bit integers, so our goal is to make these things integer and scale them to 0-255 range. And remove the last column (opacity).
# Filter out ones
mymap = mymap[:, :-1]
# Multiply by 256 and convert to uint8
mymap = np.uint8(mymap * 256)
Now we have to properly reshape it into a 16x16 array.
You actually have to reshape into (16, 16, 3), as the result would be a 3d array.
mymap = mymap.reshape(16, 16 ,3)
And, finally, make a PIL image out of that and write out
img = Image.fromarray(mymap)
img.save("output.png")
My result looks like this: ( please zoom in as it's only 16x16 pixels )

Related

Pytorch tensor changing colors

I would appreciate any help on that.
Why after putting tensor of 3d (image) into 4d tensor, the image colors changed.
p = "path/to/image"
p = Image.open(p)
p = transforms.PILToTensor()(p)
transforms.ToPILImage()(p).show() # ok (left pic)
temp = torch.zeros(4, p.size()[0], p.size()[1], p.size()[2])
temp[0] = p
transforms.ToPILImage()(temp[0]).show() # not ok (right pic)
The reason is that the first tensor p is an integer tensor and values range between 0 - 255. The second image is a float tensor and the values range between 0.0 - 255.0. imshow function expects integer values between 0 - 255 or float values between 0 - 1, you can read more here.
To fix this problem, you have two options either add the dtype=torch.uint8 when you define a temp tensor or divide the values of the tensor by 255 to scale it between 0 -1.
# cell 1
from PIL import Image
from torchvision import transforms
import torch
from matplotlib import pyplot as plt
p = Image.open("pi.png")
p = transforms.PILToTensor()(p).permute(1, 2, 0)
plt.imshow( p ) #ok
# cell 2
temp = torch.zeros(4, p.size()[0], p.size()[1], p.size()[2], dtype=torch.uint8)
temp[0] = p
plt.imshow(temp[0]) # or you can use plt.imshow(temp[0]/255)

Using colormap to create a PIL Image from the luminosity map of an image

I have M vectors containing 4096 data points between 0 and 1 that represent the luminosity map of faces. This is a sample of the actual images.
Now, my purpose is to put them in a plotly visual, but to do so I need to provide a PIL object representing the image, This is my MVC
import PIL.Image as pilim
import matplotlib.cm as cm
import numpy as np
greys = cm.get_cmap('Greys')
num_images = 10
num_faces = faces.shape[0]
sample_images = np.random.choice(num_faces, num_images, replace=False)
for index in sample_images:
greyscale = np.apply_along_axis(greys, 0, faces[index]).reshape((64, 64, 4))
im = pilim.fromarray(greyscale, mode='RGBA')
im.save('test{}.png'.format(index)) greys = cm.get_cmap('Greys')
Faces is a ndarray with 698 samples. Something like the following sample
[[0.01617647 0.01617647 0.01617647 ... 0. 0. 0. ]
[0.01617647 0.01617647 0.01617647 ... 0. 0. 0. ]
[0.01617647 0.01617647 0.01617647 ... 0. 0. 0. ]]
and this is my depressing result
PIL works with pixel data, so each of RGBA is a value from 0 to 255. A colormap default generates its RGBA values in the range 0-1. To convert them, you could multiply those by 255 and convert to 8 bit unsigned integers (uint8), like so:
greyscale = np.uint8(cmap(faces[index].reshape(64,64)) * 255)
But, matplotlib's colormaps also support a parameter to directly generate those bytes:
greyscale = cmap(faces[index].reshape(64,64), bytes=True)
You could reshape your arrays afterwards to (64,64,4), but it is easier and more readable to do the conversion before applying the colormap.
There is a choice of several sequential colormaps for this type of images. Appending an _r to the name gives the reverse colormap (so dark and light reversed).
Here is some code to get you started:
import PIL.Image as pilim
import matplotlib.cm as cm
import numpy as np
from matplotlib import pyplot as plt
cmap = cm.get_cmap('copper_r') # 'bone_r', 'Greys', 'copper_r', 'Purple', ...
num_images = 1
faces = np.tile(np.linspace(0,1,4096), 698).reshape(698, 4096)
num_faces = faces.shape[0]
sample_images = np.random.choice(num_faces, num_images, replace=False)
print(sample_images)
for index in sample_images:
greyscale = cmap(faces[index].reshape(64,64), bytes=True)
im = pilim.fromarray(greyscale, mode='RGBA')
im.save(f'test{index}.png')
PS: There is also an imsave function in matplotlib, which would further simplify the code:
for index in sample_images:
plt.imsave(f'test{index}.png', faces[index].reshape(64,64), cmap=cmap)
If the image would show up upside down, adding origin='lower' to imsave would reverse it.
The solution is actually pretty simple. There are two steps missing on my code:
re-scale to 0-255 values
Cast to uint8 in order to PIL to understand the array
greyscale = np.apply_along_axis(greys, 0, faces[index]).reshape((64, 64, 4))*255
greyscale = greyscale.astype(np.uint8)
im = pilim.fromarray(greyscale)
https://python-decompiler.com/article/2012-06/how-to-convert-numpy-array-to-pil-image-applying-matplotlib-colormap

Python Shape function for k-means clustering

I have one geotiff grey scale image which gave me the (4377, 6172) 2D array. In the first part, I am considering (:1024, :1024) values(Total values are -> 1024 * 1024 = 1048576) for my compression algorithm. Through this algorithm, I am getting total 4 values in finalmatrix list var through the algorithm. After this, I am applying K-means algorithm on that values. A program is below :
import numpy as np
from osgeo import gdal
from sklearn import cluster
import matplotlib.pyplot as plt
dataset =gdal.Open("1.tif")
band = dataset.GetRasterBand(1)
img = band.ReadAsArray()
finalmat = [255, 0, 2, 2]
#Converting list to array for dimensional change
ay = np.asarray(finalmat).reshape(-1,1)
fig = plt.figure()
k_means = cluster.KMeans(n_clusters=2)
k_means.fit(ay)
cluster_means = k_means.cluster_centers_.squeeze()
a_clustered = k_means.labels_
print('# of observation :',ay.shape)
print('Cluster Means : ', cluster_means)
a_clustered.shape= img.shape
fig=plt.figure(figsize=(125,125))
ax = plt.subplot(2,4,8)
plt.axis('off')
xlabel = str(1) , ' clusters'
ax.set_title(xlabel)
plt.imshow(a_clustered)
plt.show()
fig.savefig('kmeans-1 clust ndvi08jan2010_guj 12 .png')
In the above Program I am getting error in the line a_clustered.shape= img.shape. The error which I am getting is below:
Error line:
a_clustered.shape= img.shape
ValueError: cannot reshape array of size 4 into shape (4377,6172)
<matplotlib.figure.Figure at 0x7fb7c63975c0>
Actually, I want to visualize the clustering on Original image through compressed value which I am getting. Can you please give suggestion what to do
It does not make a lot of sense to use KMeans on 1 dimensional data.
And it makes even less sense to use it on a 4 x 1 array!
Your site then comes from the fact that you can't just resize a 4 x 1 integer array into a large picture.
Just print the array a_clustered you are trying to plot. It probably contains [0, 1, 1, 1].

Index 150 out of bounds in axis0 with size 1

I was making histogram using numpy array in Python with open cv. The code is as follows:
#finding histogram of an image
import numpy as np
import cv2
img = cv2.imread("cr7.jpg")
gry_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
a=np.zeros((1,256),dtype=np.uint8)
#finding how many times a particular pixel intensity repeats
for x in range (0,183): #size of gray_img is (184,275)
for y in range (0,274):
g=gry_ img[x,y]
a[g]=a[g]+1
print(a)
Error is as follows:
IndexError: index 150 is out of bounds for axis 0 with size 1
Since you haven't supplied the image, it is only from guessing that it seems you've made a mistake with the dimensions of the image. Alternatively the issue is entirely with the shape of your results array a.
The code you have is rather fragile, and here is a cleaner way to interact with images. I use an image from opencv's data directory: aero1.jpg.
The code here resolves both potential issues identified above, whichever one it was:
fname = 'aero1.jpg'
im = cv2.imread(fname)
gry_img = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
gry_img.shape
>>> (480, 640)
# note that the image is 640pix wide by 480 tall;
# the numpy array shows the number of rows first.
# rows are in y / columns are in x
# NOTE the results array `a` need only be 1-dimensional, not 2d (1x256)
a=np.zeros((256, ), dtype=np.uint8)
# iterating over all pixels, whatever the shape of the image.
height, width = gry_img.shape
for x in xrange(width):
for y in xrange(height):
g = gry_img[y, x] # NOTE y, x not x, y
a[g] += 1
But note that you could also achieve this easily with a numpy function np.histogram (docs), with slightly careful handling of the bin edges.
histb, bin_edges = np.histogram(gry_img.reshape(-1), bins=xrange(0, 257))
# check that we arrived at the same result as iterating manually:
(a == histb).all()
>>> True

Theano Reshaping

I am unable to clearly comprehend theano's reshape. I have an image matrix of shape:
[batch_size, stack1_size, stack2_size, height, width]
, where there are stack2_size stacks of images, each having stack1_size of channels. I now want to convert them into the following shape:
[batch_size, stack1_size*stack2_size, 1 , height, width]
such that all the stacks will be combined together into one stack of all channels. I am not sure if reshape will do this for me. I see that reshape seems to not lexicographically order the pixels if they are mixed in dimensions in the middle. I have been trying to achieve this with a combination of dimshuffle,reshape and concatenate, but to no avail. I would appreciate some help.
Thanks.
Theano reshape works just like numpy reshape with its default order, i.e. 'C':
ā€˜Cā€™ means to read / write the elements using C-like index order, with
the last axis index changing fastest, back to the first axis index
changing slowest.
Here's an example showing that the image pixels remain in the same order after a reshape via either numpy or Theano.
import numpy
import theano
import theano.tensor
def main():
batch_size = 2
stack1_size = 3
stack2_size = 4
height = 5
width = 6
data = numpy.arange(batch_size * stack1_size * stack2_size * height * width).reshape(
(batch_size, stack1_size, stack2_size, height, width))
reshaped_data = data.reshape([batch_size, stack1_size * stack2_size, 1, height, width])
print data[0, 0, 0]
print reshaped_data[0, 0, 0]
x = theano.tensor.TensorType('int64', (False,) * 5)()
reshaped_x = x.reshape((x.shape[0], x.shape[1] * x.shape[2], 1, x.shape[3], x.shape[4]))
f = theano.function(inputs=[x], outputs=reshaped_x)
print f(data)[0, 0, 0]
main()

Resources