Using base64 encoding on images gives the opportunity to fully restore the image to its original shape, with all its dimension (2D, RGB) without knowing the resolutions directly - they are stored inside the base64 information
However, when I have a numpy array representing an image like:
test_image = np.random.rand(10,10,3)
and then put it into base64 with:
b64_test_image = base64.b64encode(test_image)
I am able to get back to the content of the array with:
decoded = base64.b64decode(b64_test_image)
test_image_1D = np.frombuffer(decoded)
However, test_image_1D is only one-dimensional in comparison to the orignal image which had the dimension 10x10x3. Is it possible to restore the orignal array without knowing the buffer size, like it is the case with images?
Assuming your data is always an image, you need to use a library to get the image back from the base64 encoded string. For example with OpenCV:
retval, buffer = cv2.imencode('.jpg', test_image)
jpg_as_text = base64.b64encode(buffer)
nparr = np.fromstring(base64.b64decode(jpg_as_text), np.uint8)
img2 = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
Related
Python wand supports converting images directly to a Numpy arrays, such as can be seen in related questions.
However, when doing this for .hdr (high dynamic range) images, this appears to compress the image to 0/255. As a result, converting from a Python Wand image to a np array and back drastically reduces file size/quality.
# Without converting to a numpy array
img = Image('image.hdr') # Open with Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
Running this opens the image and saves it again, which creates a file with a size of 41.512kb. However, if we convert it to numpy before saving it again..
# With converting to a numpy array
img = Image(filename=os.path.join(path, 'N_SYNS_89.hdr')) # Open with Python Wand Image
arr = np.asarray(img, dtype='float32') # convert to np array
img = Image.from_array(arr) # convert back to Python Wand Image
img.save(filename='test.hdr') # Save with Python wand
This results in a file with a size of 5.186kb.
Indeed, if I look at arr.min() and arr.max() I see that the min and max values for the numpy array are 0 and 255. If I open the .hdr image with cv2 however as an numpy array, the range is much higher.
img = cv2.imread('image.hdr'), -1)
img.min() # returns 0
img.max() # returns 868352.0
Is there a way to convert back and forth between numpy arrays and Wand images without this loss?
As per the comment of #LudvigH, the following worked as in this answer.
img = Image(filename='image.hdr'))
img.format = 'rgb'
img.alpha_channel = False # was not required for me, including it for completion
img_array = np.asarray(bytearray(img.make_blob()), dtype='float32')
Now we much reshape the returned img_array. In my case I could not run the following
img_array.reshape(img.shape)
Instead, for my img.size was a (x,y) tuple that should have been an (x,y,z) tuple.
n_channels = img_array.size / img.size[0] / img.size[1]
img_array = img_array.reshape(img.size[0],img.size[1],int(n_channels))
After manually calculating z as above, it worked fine. Perhaps this is also what caused the original fault in converting using arr = np.asarray(img, dtype='float32')
I am trying to read a image file using PIL and then obtaining the raw pixel values in form of numpy array and then i am trying to put together the values to form a copy of original image. The code does not produce any runtime error but the image formed ("my.png") is unreadable.
from PIL import Image
import numpy as np
img_filename = "image.png"
img = Image.open(img_filename)
img = img.convert("RGB")
img.show()
aa = np.array(img.getdata())
alpha = Image.fromarray(aa,"RGB")
alpha.save('my.png')
alpha.show()
np.array(img.getdata()) gives a 2D array of shape (X, 3), where X depends on the dimensions of the original image.
Just change the relevant line of code to:
aa = np.array(img)
This will assign a 3D array to aa, and thus solve your problem.
I converted an image to numpy array using:
arr = np.array(PIL.Image.open('1.jpg'))
Then I modified part of the array:
arr[0][0][0] = 128
and converted the array back to an image:
img = PIL.Image.fromarray(np.uint8(arr))
im.save('2.jpg')
Then, I converted the 2.jpg image into numpy array and checked value of arr:
arr = np.array(PIL.Image.open('2.jpg'))
print(arr)
I am getting a completely different array than I got before.
Why is this happening?
The way you save the image affects the results.
The jpg compresses the image and alters the values.
About the image formats see here:
http://pillow.readthedocs.io/en/3.1.x/handbook/image-file-formats.html
Use this:
arr = np.array(PIL.Image.open('1.jpg')
arr[0][0][0] = 128
img = PIL.Image.fromarray(np.uint8(arr))
im.save('2.bmp')
arr2 = np.array(PIL.Image.open('2.bmp'))
print(arr)
print(arr2)
This works fine.
Because .jpg is not lossless image format.
If you want to save image as is, save as lossless image format like bmp, tiff, etc.
The reason that your arrays don't match is that you are storing the image as JPEG and this is a lossy format - the two images are visually identical but have been compressed.
If you save your image as a bitmap then load it into an array, they will be identical.
I have an image that I have encoded and sent out using protobuf like so:
message.image = numpy.ndarray.tobytes(image)
when I receive and parse that message I use this:
image_array = numpy.frombuffer(request.image, numpy.uint8)
This gives me a one-dimensional array. I cannot get this back into an image format. I have tried using numpy's reshape command like so but with no luck:
image = image_array.reshape( 400, 600, 3 )
The image being sent is 400x600 pixels and it is a 3 channel color image. Any suggestions on what I am missing?
You would also need to store the img.shape data of the original image you wanted to encode and whole decoding you need that img.shape value to reshape the matrix to it's original form as:
import numpy as np
# Create a dummy matrix
img = np.ones((50, 50, 3), dtype=np.uint8) * 255
# Save the shape of original matrix.
img_shape = img.shape
message_image = np.ndarray.tobytes(img)
re_img = np.frombuffer(message_image, dtype=np.uint8)
# Convert back the data to original image shape.
re_img = np.reshape(re_img, img_shape)
I'm trying to open an RGB picture, convert it to grayscale, then represent it as a list of floats scaled from 0 to 1. At last, I want to convert it back again to an Image. However, in the code below, something in my conversion procedure fails, as img.show() (the original image) displays correctly while img2.show() display an all black picture. What am I missing?
import numpy as np
from PIL import Image
ocr_img_path = "./ocr-test.jpg"
# Open image, convert to grayscale
img = Image.open(ocr_img_path).convert("L")
# Convert to list
img_data = img.getdata()
img_as_list = np.asarray(img_data, dtype=float) / 255
img_as_list = img_as_list.reshape(img.size)
# Convert back to image
img_mul = img_as_list * 255
img_ints = np.rint(img_mul)
img2 = Image.new("L", img_as_list.shape)
img2.putdata(img_ints.astype(int))
img.show()
img2.show()
The image used
The solution is to flatten the array before putting it into the image. I think PIL interprets multidimensional arrays as different color bands.
img2.putdata(img_ints.astype(int).flatten())
For a more efficient way of loading images, check out
https://blog.eduardovalle.com/2015/08/25/input-images-theano/
but use image.tobytes() (Pillow) instead of image.tostring() (PIL).
.