I had to write this recently and ended up with
indices = np.dstack(np.indices(im.shape[:2]))
data = np.concatenate((im, indices), axis=-1)
Where im is a numpy array. You are probably better off reading the images straight into numpy arrays with
from scipy.misc import imread
im = imread("farm.jpg")
Or, better still if you have Scikit Image installed
from skimage.io import imread
im = imread("farm.jpg")
Answer from YXD on Stack OverflowI had to write this recently and ended up with
indices = np.dstack(np.indices(im.shape[:2]))
data = np.concatenate((im, indices), axis=-1)
Where im is a numpy array. You are probably better off reading the images straight into numpy arrays with
from scipy.misc import imread
im = imread("farm.jpg")
Or, better still if you have Scikit Image installed
from skimage.io import imread
im = imread("farm.jpg")
I am not sure if this is the very efficient. But here you go, say arr = np.array(im); then you can do something like this.
>>> arr = np.arange(150).reshape(5, 10, 3)
>>> x, y, z = arr.shape
>>> indices = np.vstack(np.unravel_index(np.arange(x*y), (y, x))).T
#or indices = np.hstack((np.repeat(np.arange(y), x)[:,np.newaxis], np.tile(np.arange(x), y)[:,np.newaxis]))
>>> np.hstack((arr.reshape(x*y, z), indices))
array([[ 0, 1, 2, 0, 0],
[ 3, 4, 5, 0, 1],
[ 6, 7, 8, 0, 2],
[ 9, 10, 11, 0, 3],
[ 12, 13, 14, 0, 4],
[ 15, 16, 17, 1, 0],
[ 18, 19, 20, 1, 1],
[ 21, 22, 23, 1, 2],
[ 24, 25, 26, 1, 3],
[ 27, 28, 29, 1, 4],
[ 30, 31, 32, 2, 0],
[ 33, 34, 35, 2, 1],
[ 36, 37, 38, 2, 2],
...
[129, 130, 131, 8, 3],
[132, 133, 134, 8, 4],
[135, 136, 137, 9, 0],
[138, 139, 140, 9, 1],
[141, 142, 143, 9, 2],
[144, 145, 146, 9, 3],
[147, 148, 149, 9, 4]])
I need to do the following:
-
Import an image.
-
Convert image to a 2D array of pixel values (rgb tuples, bytes, whatever it doesn't matter).
-
Manipulate that 2D array (needs to be 2D as I'm using a library that requires a 2D array of data).
-
Convert the newly changed 2D array back into an image.
-
Save the image.
Anyone know how I can accomplish this? PIL Image documentation has some functions to get data but it's all 1D arrays, and I'm not sure how to get them into 2D arrays and then re-convert it back into an image again.
How can I convert an array of images to a 2D array in Python - Stack Overflow
Convert a binary image to 2D array or Matrix in Python? - Stack Overflow
python - Converting a PNG image to 2D array - Stack Overflow
python - Converting 2D Numpy array of grayscale values to a PIL image - Stack Overflow
First convert the RGB values in the last dimension to the HEX value using whatever function you like. This SO answer may help.
Reshape then works on any number of dimensions:
import numpy as np
def rgb2hex(r, g, b):
return '#%02x%02x%02x' % (r, g, b)
vfunc = np.vectorize(rgb2hex)
a = (np.random.uniform(0,1,(10,5,5,3))*255).astype(int)
c = vfunc(a[:,:,:,0], a[:,:,:,1], a[:,:,:,2])
c.reshape((10,25))
In order to do so, you firstly need to reshape the ndarray (np.reshape):
a = np.random.randint(1,10,(500, 32, 32, 3))
a_r = np.reshape(a, (500, 1024, 3))
print(a_r.shape)
# (500, 1024, 3)
Now, in order to convert the RGB values along the last dimension to hexadecimal representation as you suggest, you could define a function that returns a hexadecimal representation of the three values with a simple string formatting:
def rgb_to_hex(x):
return '#{:02X}{:02X}{:02X}'.format(*rgb.reshape(3))
In order to apply the conversion along all rows in the last axis, you can use np.apply_along_axis:
a_new = np.apply_along_axis(rgb2hex, axis=-1, arr=a_r).shape
print(a_new.shape)
# (500, 1024)
Using cv2 -Read more here
import cv2
img = cv2.imread('path/to/img.jpg')
resized = cv2.resize(img, (128, 128), cv2.INTER_LINEAR)
print pic
Using skimage - Read more here
import skimage.io as skio
faces = skio.imread_collection('path/to/images/*.pgm',conserve_memory=True) # can load multiple images at once
Using Scipy - Read more here
from scipy import misc
pic = misc.imread('path/to/img.jpg')
print pic
Plotting Images
import matplotlib.pyplot as plt
plt.imshow(faces[0],cmap=plt.cm.gray_r,interpolation="nearest")
plt.show()
example I am currently working with.
"""
A set of utilities that are helpful for working with images. These are utilities
needed to actually apply the seam carving algorithm to images
"""
from PIL import Image
class Color:
"""
A simple class representing an RGB value.
"""
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
def __repr__(self):
return f'Color({self.r}, {self.g}, {self.b})'
def __str__(self):
return repr(self)
def read_image_into_array(filename):
"""
Read the given image into a 2D array of pixels. The result is an array,
where each element represents a row. Each row is an array, where each
element is a color.
See: Color
"""
img = Image.open(filename, 'r')
w, h = img.size
pixels = list(Color(*pixel) for pixel in img.getdata())
return [pixels[n:(n + w)] for n in range(0, w * h, w)]
def write_array_into_image(pixels, filename):
"""
Write the given 2D array of pixels into an image with the given filename.
The input pixels are represented as an array, where each element is a row.
Each row is an array, where each element is a color.
See: Color
"""
h = len(pixels)
w = len(pixels[0])
img = Image.new('RGB', (w, h))
output_pixels = img.load()
for y, row in enumerate(pixels):
for x, color in enumerate(row):
output_pixels[x, y] = (color.r, color.g, color.b)
img.save(filename)
There would be several ways to do the comparison to give us a boolean array and then, we just need to convert to int array with type conversion. So, for the comparison, one simple way would be to compare against 255 and check for ALL matches along the last axis. This would correspond to checking for [255, 255, 255, 255]. Thus, one approach would be like so -
((arr == 255).all(-1)).astype(int)
Sample run -
In [301]: arr
Out[301]:
array([[[255, 255, 255, 255],
[ 0, 0, 0, 255],
[ 0, 0, 0, 255]],
[[ 0, 0, 0, 255],
[255, 255, 255, 255],
[255, 255, 255, 255]]])
In [302]: ((arr == 255).all(-1)).astype(int)
Out[302]:
array([[1, 0, 0],
[0, 1, 1]])
If there are really only two values in the array as you say, simply scale and return one of the dimensions:
(arr[:,:,0] / 255).astype(int)
I think the answer is wrong. The Image.fromarray( ____ , 'L') function seems to only work properly with an array of integers between 0 and 255. I use the np.uint8 function for this.
You can see this demonstrated if you try to make a gradient.
import numpy as np
from PIL import Image
# gradient between 0 and 1 for 256*256
array = np.linspace(0,1,256*256)
# reshape to 2d
mat = np.reshape(array,(256,256))
# Creates PIL image
img = Image.fromarray(np.uint8(mat * 255) , 'L')
img.show()
Makes a clean gradient
vs
import numpy as np
from PIL import Image
# gradient between 0 and 1 for 256*256
array = np.linspace(0,1,256*256)
# reshape to 2d
mat = np.reshape(array,(256,256))
# Creates PIL image
img = Image.fromarray( mat , 'L')
img.show()
Has the same kind of artifacting.
If I understood you question, you want to get a grayscale image using PIL.
If this is the case, you do not need to multiply each pixels by 255.
The following worked for me
import numpy as np
from PIL import Image
# Creates a random image 100*100 pixels
mat = np.random.random((100,100))
# Creates PIL image
img = Image.fromarray(mat, 'L')
img.show()
If your objective is to save a numpy array as an image, your approach have a problem. The function plt.savefig saves an image of the plot, not the array. Also transforming an array into an image may carry some precision loss (when converting from float64 or float32 to uint16). That been said, I suggest you use skimage and imageio:
import imageio
import numpy as np
from skimage import img_as_uint
data = np.load('0058_00086_brown_2_recording1.wav.npy')
print("original", data.shape)
img = img_as_uint(data)
imageio.imwrite('image.png', img)
load = imageio.imread('image.png')
print("image", load.shape)
This script loads the data you provided and prints the shape for verification
data = np.load('0058_00086_brown_2_recording1.wav.npy')
print("original", data.shape)
then it transform the data to uint, saves the image as png and loads it:
img = img_as_uint(data)
imageio.imwrite('image.png', img)
load = imageio.imread('image.png')
the output of the script is:
original (64, 25)
image (64, 25)
i.e. the image is loaded with the same shape that data. Some notes:
- image.png is saved as a grayscale image
- To save to .jpg just change to
imageio.imwrite('image.jpg', img) - In the case of .png the absolute average distance from the original image was
3.890e-06(this can be verified usingnp.abs(img_as_float(load) - data).sum() / data.size)
Information about skimage and imageio can be found in the respectives websites. More on saving numpy arrays as images can be found in the following answers: [1], [2], [3] and [4].
link
from scipy.misc import imread
image_data = imread('test.jpg').astype(np.float32)
This should give you the numpy array (I would suggest using imread from scipy)
Three solutions are:
using
matplotlib:from matplotlib import pyplot as plt plt.imshow(decrypted, interpolation='nearest', cmap='gray') plt.savefig('decrypted1.png') plt.show()

using
PIL:from PIL import Image img = Image.fromarray(decrypted.astype(np.uint8), 'L') img.save('decrypted2.png') img.show()

using
cv2:import cv2 cv2.imwrite('decrypted3.png', decrypted) cv2.imshow("decrypted3", decrypted) cv2.waitKey()

If you want to have a grayscale image, then simply convert it to numpy array with uint8 dtype as in:
grayscale = np.array(decrypted, dtype=np.uint8)
On the other hand, if you want a color image (RGB), then stack three of those grayscale images along depth-wise:
decrypted_rgb = np.dstack([np.array(decrypted, dtype=np.uint8)]*3)
Having these, you can then display using matplotlib or OpenCV or pillow etc.
You need to use np.transpose to rearrange dimensions. Now, n x m x 3 is to be converted to 3 x (n*m), so send the last axis to the front and shift right the order of the remaining axes (0,1). Finally , reshape to have 3 rows. Thus, the implementation would be -
img.transpose(2,0,1).reshape(3,-1)
Sample run -
In [16]: img
Out[16]:
array([[[155, 33, 129],
[161, 218, 6]],
[[215, 142, 235],
[143, 249, 164]],
[[221, 71, 229],
[ 56, 91, 120]],
[[236, 4, 177],
[171, 105, 40]]])
In [17]: img.transpose(2,0,1).reshape(3,-1)
Out[17]:
array([[155, 161, 215, 143, 221, 56, 236, 171],
[ 33, 218, 142, 249, 71, 91, 4, 105],
[129, 6, 235, 164, 229, 120, 177, 40]])
[ORIGINAL ANSWER]
Let's say we have an array img of size m x n x 3 to transform into an array new_img of size 3 x (m*n)
Initial Solution:
new_img = img.reshape((img.shape[0]*img.shape[1]), img.shape[2])
new_img = new_img.transpose()
[EDITED ANSWER]
Flaw: The reshape starts from the first dimension and reshapes the remainder, this solution has the potential to mix the values from the third dimension. Which in the case of images could be semantically incorrect.
Adapted Solution:
# Dimensions: [m, n, 3]
new_img = new_img.transpose()
# Dimensions: [3, n, m]
new_img = img.reshape(img.shape[0], (img.shape[1]*img.shape[2]))
Strict Solution:
# Dimensions: [m, n, 3]
new_img = new_img.transpose((2, 0, 1))
# Dimensions: [3, m, n]
new_img = img.reshape(img.shape[0], (img.shape[1]*img.shape[2]))
The strict is a better way forward to account for the order of dimensions, while the results from the Adapted and Strict will be identical in terms of the values (set(new_img[0,...])), however with the order shuffled.