I want to save floating-point numbers as pixels in an image file. I am currently working in OpenCV-python, but I had also tried it with Pillow (PIL). Both packages convert float pixel data to integer before writing them to the file.
I want to save pixel values such as:
(245.7865, 123.18788, 98.9866)
But when I read back the image file I get:
(246, 123, 99)
Somehow my floating-point numbers get rounded off and converted to integers.
How to stop PIL or OpenCV from converting them to integer?
Raster images are normally stored as integer values only. Instead save the numpy array directly like so
x = numpy.array([1, 2, 3])
with open('x.npy', 'wb') as f:
numpy.save(f, x)
Then load the variable back like so
x = numpy.load('x.npy')
Other alternatives include
Save one or more GRAY16 png images, with your floats multiplied and truncated.
Use the Netpbm format supporting floats.
Save a pickle.
The behavior you observe depends on the file format in which you save the image. Few image formats have a specification for floating-point pixel values. Though some do, first and foremost TIFF.
To demonstrate the desired behavior with a TIFF image writer, consider the following script. It uses the versatile image input/output library ImageIO, which relies on PILlow as one of its back-ends:
# Use Stack Overflow logo as sample image.
import imageio
logo = 'https://cdn.sstatic.net/Sites/stackoverflow/img/logo.png'
image = imageio.imread(logo)
# Normalize to 1. Pixel values are now floating-point.
image = image / image.max()
# Save as image file and read back in.
format = 'tiff'
imageio.imwrite(f'image.{format}', image)
print(f'wrote: {image.dtype}')
image = imageio.imread(f'image.{format}')
print(f'read: {image.dtype}')
The output of that script is:
wrote: float64
read: float64
If, on the other hand, you change the format to PNG (format = 'png' in the code), the output is:
Lossy conversion from float64 to uint8. Range [0, 1].
Convert image to uint8 prior to saving to suppress this warning.
wrote: float64
read: uint8
Related
Sorry for my english but it's not my first language.
I would like to create a program that:
Transform a jpeg or png image into an array (very important: I would like an array composed only of the values that the pixels of the image have and not metadata or other information. Where I can select each specific pixel of the image).
Save this array in a txt file.
Transform this array composed of only the pixel values of the image back into jpg or png image and save it in a file.
Requests:
Is the array I created with the program I wrote composed only of the pixel values of the image? is there also metadata or other information?
Is this a valid way to remove metadata from an image?
Is this a valid way to create the array representing that image pixel by pixel?
Is this a valid way to convert png images to jpeg or jpeg to png?
Thank you!
This is the program I created, any opinion?
import numpy as np
from PIL import Image
import sys
img_data = Image.open("imagea.jpeg")
img_arr = np.array(img_data)
np.set_printoptions(threshold=sys.maxsize)
print(img_arr.shape)
new_img = Image.fromarray(img_arr)
new_img.save("imageb.jpeg")
print("Image saved!")
file = open("file1.txt", "w+")
content = str(img_arr)
file.write(content)
file.close()
print("Finished!")
Loading an image and converting it to a Numpy array is a perfectly legitimate way of discarding all metadata including:
EXIF data, copyright data,
IPTC and XMP data,
ICC colour profile data
You can tell it's all gone by thinking about the Numpy array you hold and its dimensions and data type.
Note that you need to be careful with PNG palette images and images with an alpha channel.
Note that you can achieve this more simply on the command-line with ImageMagick using:
magick mogrify -strip IMAGE.JPG
Or with exiftool.
Note that you can achieve this by using a format that doesn't support metadata, such as NetPBM, with extension .ppm e.g.:
magick INPUT.JPG -strip -compress none RESULT.PPM # gives P3/plain ASCII file
magick INPUT.JPG -strip RESULT.PPM # gives P6/binary file
You can also read/write PPM files with PIL.
I am reading an image from S3 bucket, then resize the image and get the numpy array of the resized image, called "a". I also save the resized image and reopen it and get the numpy array of that called "b". My question is why a and b are different?
resp = s3.get_object(Bucket=event['bucket'], Key=event['image_keys'][0])
data = resp['Body']
image_as_bytes = io.BytesIO(data.read())
image = Image.open(image_as_bytes).convert('RGB').resize((299, 299),Image.NEAREST)
a = np.asarray(image)
image.save('IMAGE_58990004_110132026B_13d64039_resized_lambda.jpg')
b = np.asarray(Image.open('IMAGE_58990004_110132026B_13d64039_resized_lambda.jpg'))
Does ".save" changes the numpy array?
Assuming that image.save(...) uses the filename ending (.jpg) to pick a file format (I don't know if it does. but it seems reasonable), then you are saving as a JPEG file, and the JPEG compression algorithm is lossy, i.e, it discards some information to make the file smaller.
Try using a file format with lossless compression, such as PNG.
I'm trying to save a 16-bit numpy array as a 16-bit PNG but what I obtain is only a black picture. I put here a minimum example of what I'm talking aboout.
im = np.random.randint(low=1, high=6536, size=65536).reshape(256,256) #sample numpy array to save as image
plt.imshow(im, cmap=plt.cm.gray)
Given the above numpy array this is the image I see with matplotlib, but when then I save the image as 16-bit png I obtain the picture below:
import imageio
imageio.imwrite('result.png', im)
Image saved:
where some light grey spots are visible but the image is substantially black. Anyway when I read back the image and visualize it again with matplotlib I see the same starting image. I also tried other libraries instead of imageio (like PIL or PyPNG) but with the same result.
I know that 16-bit image values range from 0 to 65535 and in the array numpy array here there only values from 1 to 6536, but I need to save numpy arrays images similar to this, i.e. where the maximum value represented in the image isn't the maximum representable value. I think that some sort of nornalization is involved in the saving process. I need to save the array exactly as I see them in matplotlib at their maximum resolution and without compression or shrinkage in their values (so division by 255 or conversion to 8-bit array are not suitable).
It looks like imageio.imwrite will do the right thing if you convert the data type of the array to numpy.uint16 before writing the PNG file:
imageio.imwrite('result.png', im.astype(np.uint16))
When I do that, result.png is a 16 bit gray-scale PNG file.
If you want the image to have the full grayscale range from black to white, you'll have to scale the values to the range [0, 65535]. E.g. something like:
im2 = (65535*(im - im.min())/im.ptp()).astype(np.uint16)
Then you can save that array with
imageio.imwrite('result2.png', im2)
For writing a NumPy array to a PNG file, an alternative is numpngw (a package that I created). For example,
from numpngw import write_png
im2 = (65535*(im - im.min())/im.ptp()).astype(np.uint16)
write_png('result2.png', im2)
If you are already using imageio, there is probably no signficant advantage to using numpngw. It is, however, a much lighter dependency than imageio--it depends only on NumPy (no dependence on PIL/Pillow and no dependence on libpng).
I have created an array of bits by using this:
Data = []
Bytes = numpy.fromfile(filename, dtype = "uint8")
Bits = numpy.unpackbits(Bytes)
for b in Bits:
Data.append(b)
"filename" ends with ".png".
Later on, I do some stuff with these bits. I want to save an image with another(or the same) set of bits. How do I do it? The best option would be using: saveToPNG(Data)
You can save those bits as a PNG file by simply reversing the steps you've used.
BTW, there's no need to create the Data list: you can access the bits in the Bits array with normal Python functions & operators as well as with Numpy. But if you really do want those bits in a plain Python list then there's no need for that slow for ... append loop: just pass the array to the list constructor.
I've changed your variable names to make them conform to the PEP-8 style guide.
import numpy as np
# File names
in_name = 'square.png'
out_name = 'square_out.png'
# Read data and convert to a list of bits
in_bytes = np.fromfile(in_name, dtype = "uint8")
in_bits = np.unpackbits(in_bytes)
data = list(in_bits)
# Convert the list of bits back to bytes and save
out_bits = np.array(data)
print(np.all(out_bits == in_bits))
out_bytes = np.packbits(out_bits)
print(np.all(out_bytes == in_bytes))
out_bytes.tofile(out_name)
However, I don't know why you want to do this. If you want access to the image data in the PNG file then you need to decode it properly. A simple way to do that is to use PIL (Pillow) to load the image file into a PIL Image object; Numpy can make an array from a PIL Image. You can then use standard Numpy tools to analyze or manipulate the raw image data, and then pass it back to PIL to save it as a PNG (or various other image file formats). See the final code block in this answer for an example.
I am looking to store pixel values from satellite imagery into an array. I've been using
np.empty((image_width, image_length)
and it worked for smaller subsets of an image, but when using it on the entire image (3858 x 3743) the code terminates very quickly and all I get is an array of zeros.
I load the image values into the array using a loop and opening the image with gdal
img = gdal.Open(os.path.join(fn + "\{0}".format(fname))).ReadAsArray()
but when I include print img_array I end up with just zeros.
I have tried almost every single dtype that I could find in the numpy documentation but keep getting the same result.
Is numpy unable to load this many values or is there a way to optimize the array?
I am working with 8-bit tiff images that contain NDVI (decimal) values.
Thanks
Not certain what type of images you are trying to read, but in the case of radarsat-2 images you can the following:
dataset = gdal.Open("RADARSAT_2_CALIB:SIGMA0:" + inpath + "product.xml")
S_HH = dataset.GetRasterBand(1).ReadAsArray()
S_VV = dataset.GetRasterBand(2).ReadAsArray()
# gets the intensity (Intensity = re**2+imag**2), and amplitude = sqrt(Intensity)
self.image_HH_I = numpy.real(S_HH)**2+numpy.imag(S_HH)**2
self.image_VV_I = numpy.real(S_VV)**2+numpy.imag(S_VV)**2
But that is specifically for that type of images (in this case each image contains several bands, so i need to read in each band separately with GetRasterBand(i), and than do ReadAsArray() If there is a specific GDAL driver for the type of images you want to read in, life gets very easy
If you give some more info on the type of images you want to read in, i can maybe help more specifically
Edit: did you try something like this ? (not sure if that will work on tiff, or how many bits the header is, hence the something:)
A=open(filename,"r")
B=numpy.fromfile(A,dtype='uint8')[something:].reshape(3858,3743)
C=B*1.0
A.close()
Edit: The problem is solved when using 64bit python instead of 32bit, due to memory errors at 2Gb when using the 32bit python version.