From Raw binary image data to PNG in Python - python

After searching for a few hours, I ended up on this link. A little background information follows.
I'm capturing live frames of a running embedded device via a hardware debugger. The captured frames are stored as raw binary files, without headers or format. After looking at the above link and understanding, albeit perfunctorily, the NumPY and Matplotlib, I was able to convert the raw binary data to an image successfully. This is important because I'm not sure if the link to the raw binary file will help any one.
I use this code:
import matplotlib.pyplot as plt # study documentation
import numpy as np # " "
iFile = "FramebufferL0_0.bin" # Layer-A
shape = (430, 430) # length and width of the image
dtype = np.dtype('<u2') # unsigned 16 bit little-endian.
oFile = "FramebufferL0_0.png"
fid = open(iFile, 'rb')
data = np.fromfile(fid, dtype)
image = data.reshape(shape)
plt.imshow(image, cmap = "gray")
plt.savefig(oFile)
plt.show()
Now, the image I'm showing is black and white because the color map is gray-scale (right?). The actual captured frame is NOT black and white. That is, the image I see on my embedded device is "colorful".
My question is, how can I calculate actual color of each pixel from the raw binary file? Is there a way I can get the actual color map of the image from the raw binary? I looked into this example and I'm sure that, if I'm able to calculate the R, G and B channels (and Alpha too), I'll be able to recreate the exact image. An example code would be of much help.

An RGBA image has 4 channels, one for each color and one for the alpha value. The binary file seems to have a single channel, as you don't report an error when performing the data.reshape(shape) operation (the shape for the corresponding RGBA image would be (430, 430, 4)).
I see two potential reasons:
The image actual does have colour information but when you are grabbing the data you are only grabbing one of the four channels.
The image is actually a gray-scale image, but the embedded device shows a pseudocolor image, creating the illusion of colour information. Without knowing what the colourmap is being used, it is hard to help you, other than point you towards matplotlib.pyplot.colormaps(), which lists all already available colour maps in matplotlib.
Could you
a) explain the exact source / type of imaging modality, and
b) show a photo of the output of the embedded device?
PS: Also, at least in my hands, the pasted binary file seems to have a size of 122629, which is incongruent with an image shape of (430,430).

Related

Displaying Tiff images in ipynb or jupyter notebook

I am trying to visualize a tiff image in an ipython notebook using the following code, import statements are ignored for clarity purposes.
from PIL import Image
orig_img_path = os.path.join("path/to/tiff/0.tiff")
img_orig = Image.open(orig_img_path,"r")
plt.imshow(img_orig)
plt.show()
The above snippet just shows me the following black image -
I know that the image pixel values are non-zero since I can open the original images on my MacBook and they look like the following -
I also double check that the pixel values are nonzero by using the following code of casting the PIL image to an np array using the following code and printing the array out -
img_arr = np.array(img_orig)
print(img_arr)
which gives me the following output -
I think I know what the issue is as well - that the matplotlib pyplot thinks that this is a PNG image or something like that and as we can see the pixel values are very small to be perceivable by the human eyes. I mean I can simply multiply the image by 255 which gives the following image as expected, although scaling by 255 is not entirely right since the pixel values need to be normalized for the minimum to corrspond to 0 and max to 255 assuming mat plot lib thinks that it is showing a PNG image -
I would like to know if there is a better way of doing so rather than me manually doing it all the time, any native way of displaying tiff in a Jupyter/ipython notebook which preserves all the good properties of tiff images such as floating point numbers etc.
Thanks and please let me know if anything is unclear.
Edit 1: Link to the original file is here - https://drive.google.com/file/d/1O1-QM6aeU5-QZhT36vOMjDND2vkZNgqB/view?usp=sharing
-- Megh
If you share your original image in .tiff format the solution might be more precise.
You are reading the image pixels as type float: 0-1, and after that, you parse them as uint8 : 0-255 which will turn all pixels values into 0 or 1: Black or almost Black
You can try the following approach to read your image (supposedly Black and White) and parse it:
import cv2
gray = cv2.imread("path/to/tiff/0.tiff", cv2.IMREAD_UNCHANGED)
cv2.namedWindow("MyImage", cv2.WINDOW_NORMAL)
cv2.imshow("MyImage", gray)
cv2.waitKey(0)
What is the range of values that you expect in that image? Did you do any preprocessing to it?
The image you are trying to display in matplotlib contains negative values. Visualizing works well for any kind of uint8 data (Grayscale and RGB) and data in the range of 0 - 1. Your displaying issue can be addressed by adding the min value of the image and then dividing by the max value (effectively normalizing your data to the range 0-1).
[...]
img = np.array(img_orig, dtype=float)
img += abs(np.min(img))
img /= np.max(img)
plt.imshow(img)
plt.show()

Python: How do I read the data in this multipage TIFF file to produce the desired image?

I am working with TIFF files that represent the readings of detectors in electron microscopy, and I know how this particular image should look, but I'm unsure how to get that result from the raw data in question. The TIFF files in question have several pages corresponding to frames on which data was taken, but when I look at each individual frame, they seem more or less like white noise, so there must be some other way to massage the data to look how it's meant to. I've tried reading each frame into a numpy array and taking the sum over all frames to produce a new image, and it seemed like this almost worked for some of the images in question, though not all. Preferably, I'd like to produce a numpy array representing the new image that looks as it is meant to.
The actual TIFF image itself is too large to attach here, so I'll link to where it can be downloaded on the EMPIAR database. It is /data/ds1_tifs/20180309_Vn_ribosome_0001.tif on this page, the first image listed under ds1_tifs. You'll want to unselect everything else and download this image alone, since the full dataset is obviously absurdly large. The result image should look like this.
My posts on cryo-em discussion boards haven't gained much traction, so any help would be appreciated.
"produce a numpy array representing the new image that looks as it is meant to.": The PNG image looks like a thumbnail image, a somewhat arbitrary preview of the data in the TIFF file obtained by binning and scaling:
import tifffile
im = tifffile.imread('20180309_Vn_ribosome_0001.tif', maxworkers=6)
binsize = 25
height = im.shape[1] // binsize
width = im.shape[2] // binsize
im = im[:, :height * binsize, : width * binsize]
im = im.reshape(im.shape[0], height, binsize, width, binsize)
im = im.sum((0, 2, 4), dtype='uint32')
from matplotlib import pyplot
pyplot.imshow(im, cmap='gray')
pyplot.show()

DNG raw pictures imported as 16 bit deep but unexpected plt.show() result

Trying to process raw DNG pictures in Python with rawpy ends with strange results.
import rawpy
import imageio
from matplotlib import pyplot as plt
path = '/home/stefan/AIJ/RAW.DNG'
with rawpy.imread(path) as raw:
rgb = raw.postprocess()
plt.imshow(rgb)
plt.show()
The result is an rgb picture array with 8-bit values while my camera generates 14 bit raw pictures.
Visualizing the rgb array gives an expected result:
From some googleing I understood that it is possible to import the same file but with an output in 16-bit.
I used the following parameters in the postprocess function:
rgb = raw.postprocess(output_bps=16,demosaic_algorithm=None,output_color = rawpy.ColorSpace.Adobe)
Now the rgb array contains 16 bit values but visualizing results in the following:
Could someone tell me how I could obtain a visualization similar to the first result but handling 16-bit values?
Initially I thought it was related to the fact that my camera is producing 14 bit images rather than 16 bit, but changing the parameter output_bps into 14 gives even worse visualization results.
Thanks in advance!
On request, I would add here the raw picture from a PENTAX K-5 but it is 18MB big and the forum has a limit of 2MB (may be another way to pass you the file?).
I don't think the issue has to do with how you are reading the image, as imshow does not display 16-bit RGB images. So, if you are interested in visually checking the results of reading in the 16-bit image, I would suggest either inspecting bands individually, with
plt.imshow(rgb[:, :, 0])
and so on for each band; or converting the rgb to 8-bit and displaying that, with
rgb8 = (rgb / 256).astype('uint8')
plt.imshow(rgb8)

Working with truncated images with PIL

I am trying to get the Python 2.7 PIL Library to work with JPEG images that are only available as a stream coming from a HDD image and are not complete.
I have set the option:
ImageFile.LOAD_TRUNCATED_IMAGES = True
And load the stream as far as it is available (or better said: as far as I am 100% sure that this data is still a image, not some other file type). I have tested different things and as far as I can tell (for JPEGs) PIL only accepts it as a valid JPEG Image if it finds the 0xFFDA (Start of Scan Marker). This is a short example of how I load the data:
from PIL import Image
from StringIO import StringIO
ImageFile.LOAD_TRUNCATED_IMAGES = True
with open("/path/to/image.raw", 'rb') as fp:
fp.seek("""jump to position in image where JPEG starts""")
data = fp.read("""number of bytes I know that those belong to that jpeg""")
img = Image.open(StringIO(data)) # This would throw exception if the data does
# not contain the 0xffda marker
pixel = img.load() # Would throw exception if LOAD_TRUNCATED_IMAGES = false
height,width = img.size
for i in range(height):
for j in range(width):
print pixel[i,j]
On the very last line I expected (or hoped) to see at least the read pixel data to be displayed. But for every pixel it returns (0,0,0).
The Question: Is what I am trying here not possible with PIL?
Some weeks ago I tried the same with a image file I truncated myself, simply by cutting data from it with an editor. It worked for the pixel-data that was available. As soon as it reached a pixel that I cut off, the program threw an exception (I will try this again later today to make sure that I am not remembering wrong).
If somebody is wondering why I am doing this: I need to make sure that the image/picture inside that hdd image is in consecutive blocks/clusters and is not fragmented. To make sure of this I wanted to use pixel matching.
EDIT:
I have tried it again and this is what I have seen.
I opened a truncated image in GIMP and it showed me a few pixel lines in the upper part, but PIL was not able to at least give me the RGB values of those pixels. It always returns (0,0,0).
I made the image slightly bigger such that the lower 4/5 of the image was not visible, but that was enough for PIL to show me the RGB values that were available. Everything else was (0,0,0).
I am still not 100% sure whether PIL can show me the RGB values, even if only view pixel-data is available.
I would try it with an uncompressed format like TGA. JPG being a compressed format may not make any sense to extract pixels from an incomplete image. JPEG actually stores the parameters for equations that describe the image, not pixel values. When you query a JPEG for a pixel value it evaluates the equations at that point and returns the result.
I have the same problem with Pillow==9.2.0
Let's downgrade to Pillow==8.3.2 and it works.
I don't really know about streaming, but I think that you simply cannot access rgb value the way you do.
Try:
rgb_im = img.convert('RGB')
r, g, b = rgb_im.getpixel((i, j))

python mahotas.imread reads a 2d image as 3d

I have an image saved by another code of mine. The image is a normal JPG file. I saved it with imsave.
now when I'm reading it in another code, it seems to be 3d :S
the image is here.
and a simple code to read it is this :
import mahotas
img = mahotas.imread('d:/normal.jpg')
print img.shape, img.dtype
Try reading the jpg as greyscale like this:
mahotas.imread('d:/normal.jpg', as_grey = True)
(Author of mahotas here).
The suggestion by Junuxx is correct:
mahotas.imread('file.jpg', as_grey=True)
This reads the RGB file and converts it to grey scale by a weighted average of the components (they are not equally weighted, but use typical coefficients that attempt to be perceptually more accurate).
The alternative (which I rather prefer) is:
im = mahotas.imread('file.jpg')
im = im[:,:,0]
I assume that all the channels have the same values and just use the first one.

Categories