Saving .tif Image Incorrectly using Python3 and imageio - python

I am trying to save an altered z-stack .tif file in Python3. Here's my code where I checked that the the functions worked as intended.
#libraries
import imageio as ii
#import initial image
fname='101_nuc1syg1.tif'
adata = ii.volread(fname)
#check to make sure volread works
ii.volsave('temp.tif', adata)
Which results in this:
And now when I try to do a simple threshold, using the following code:
#now doing very simple thresholding
bdata = adata < adata[0].mean()
bdata = bdata +0
ii.volsave('temp.tif', bdata)
I get this:
Any idea how to save a tif file properly after performing image operators on it?
EDIT: Note that I am able to extract each stack and save them as separate .png files, but I would prefer to have them as a single .tif file.
Data from: https://www.nature.com/articles/s41467-020-15987-2

It looks like you need to convert bdata type to np.uint8 and multiply the result by 255.
The type of the expression (adata < adata[0].mean()) is np.bool.
The common image type is np.uint8.
Convert (adata < adata[0].mean()) to type np.uint8:
bdata = (adata < adata[0].mean()).astype(np.uint8)
When converting the result to np.uint8, all True elements are converted to 1 and False elements are converted to 0.
Multiply by 255 for converting the True elements to 255 (white color):
bdata = bdata * 255
Complete code:
import imageio as ii
import numpy as np
#import initial image
fname='101_nuc1syg1.tif'
adata = ii.volread(fname)
#now doing very simple thresholding
bdata = (adata < adata[0].mean()).astype(np.uint8)
bdata = bdata * 255 # Convert ones to 255 (255 is white color)
ii.volsave('temp.tif', bdata)
Note:
I could not test my answer - the link you have posted doesn't contain a link to an image.

Related

Convert python wand hdr image to numpy array and back

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')

convert image saved in hexadecimal in a np.array to import it in opencv

I get an image stored as an object from a camera that look like this (here reduced to make it understandable):
image = np.array([['#49312E', '#4A3327', '#493228', '#472F2A'],
['#452C29', '#49312E', '#4B3427', '#49312A'],
['#473026', '#472F2C', '#48302B', '#4C342B']])
is it possible to 'import' it as an 'image' in opencv?
I tried to look at the documentation of cv2.imdecode but could get it to work.
I could preprocess this array to get it to another format but I am not sure what could 'fit' to opencv.
Thank you for your help
This is a very succinct and pythonic (using NumPy) way to implement a conversion from your hexadecimal values matrix to an RGB matrix that could be read by OpenCV.
image = np.array([['#49312E', '#4A3327', '#493228', '#472F2A'],
['#452C29', '#49312E', '#4B3427', '#49312A'],
['#473026', '#472F2C', '#48302B', '#4C342B']])
def to_rgb(v):
return np.array([np.int(v[1:3],16), np.int(v[3:5],16) , np.int(v[5:7],16)])
image_cv = np.array([to_rgb(h) for h in image.flatten()]).reshape(3, 4, 3)
cv2.imwrite('result.png', image_cv)
OpenCV requires either a RGB or a BGR input, which is to say you need to give the values of Red Green Blue or Blue Green Red on a scale from 0-255 (8 bit). I have shared with you the code to convert your array to an image.
Initially, I count the number of rows to find the height in terms of pixels. Then I count the number of items in a row to find the width.
Then I create an empty array of the given dimensions using np.zeros.
I then go to each cell and convert the hex code to its RGB equivalent, using the following formula #RRGGBB, R = int(RR,16), G = int(GG, 16), B = int(BB, 16). This converts the hexadecimal string to int.
#!/usr/bin/env python3
import numpy as np
import re
import cv2
# Your image
image = np.array([['#49312E', '#4A3327', '#493228', '#472F2A'],
['#452C29', '#49312E', '#4B3427', '#49312A'],
['#473026', '#472F2C', '#48302B', '#4C342B']])
# Enter the image height and width
height = int(len(image[0]))
width = int(len(image[0][0]))
# Create numpy array of BGR triplets
im = np.zeros((height,width,3), dtype=np.uint8)
for row in range (height):
for col in range(width):
hex = image[row, col][1:]
R = int(hex[0:2],16)
G = int(hex[2:4],16)
B = int(hex[4:6],16)
im[row,col] = (B,G,R)
# Save to disk
cv2.imwrite('result.png', im)

PIL Image convert from I mode to P mode

I have this depth image:
that I load with PIL like:
depth_image = Image.open('stereo.png')
If I print the mode of the image it shows mode I, that is (32-bit signed integer pixels) according to the documentation.
This is correct since the image values range from 0 to 255. I'd like to colorize this depth image for better visualization so I tried to convert it to P mode with a palette like:
depth_image = depth_image.convert('P', palette=custom_palette)
depth_image.save("colorized.png")
But the result is a black and white image like this:
I'm sure the palette is ok, since there are 256 colors in int format all in a single array.
I've tried to convert it to RGB before saving like:
depth_image = depth_image.convert('RGB')
Also I tried adding the palette afterwards like:
depth_image = depth_image.putpalette(custom_palette)
And if I try to save it without converting it to RGB I get a:
depth_image.save("here.png")
AttributeError: 'NoneType' object has no attribute 'save'
So far I'll try converting the image to a numpy array and then map the colors from there, but I was wondering what was I missing out regarding PIL. I was looking around the documentation but didn't find much regarding I to P conversion.
I think the issue is that your values are scaled to the range 0..65535 rather than 0..255.
If you do this, you will see the values are larger than you expected:
i = Image.open('depth.png')
n = np.array(i)
print(n.max(),n.mean())
# prints 32257, 6437.173
So, I quickly tried:
n = (n/256).astype(np.uint8)
r = Image.fromarray(n)
r=r.convert('P')
r.putpalette(custom_palette) # I grabbed this from your pastebin

Convert an array of RGB hexadecimal values to OpenCV image in Python

In the input of a program is given height amount of lines that have width amount of RRGGBB values in them, with RR/GG/BB being a hexadecimal value of the corresponding color in an RGB format.
I need to take the input and convert it to an OpenCV image so that I could interact with it using the OpenCV library. How would I accomplish this?
Example of input:
https://drive.google.com/file/d/1XuKRuAiQLUv4rbVxl2xTgqYr_8JQeu63/view?usp=sharing
The first number is height, second is width, the rest of the text file is the image itself.
That is a really inefficient way to store an image, and this is a correspondingly inefficient way to unpack it!
#!/usr/bin/env python3
import numpy as np
import re
import cv2
# Read in entire file
with open('in.txt') as f:
s = f.read()
# Find anything that looks like numbers
l=re.findall(r'[0-9a-f]+',s)
# Determine height and width
height = int(l[0])
width = int(l[1])
# Create numpy array of BGR triplets
im = np.zeros((height,width,3), dtype=np.uint8)
i = 2
for row in range (height):
for col in range(width):
hex = l[i]
R = int(hex[0:2],16)
G = int(hex[2:4],16)
B = int(hex[4:6],16)
im[row,col] = (B,G,R)
i = i+1
# Save to disk
cv2.imwrite('result.png', im)
In case the data file disappears in future, this is how the first few lines look:
1080 1920
232215 18180b 18170b 18180b 18170a 181609 181708 171708 15160c 14170d
15170d 16170d 16160d 16170d 16170d 16170d 15160d 15160d 17170e 17180f
17180f 18180f 191a11 191a12 1c1c0f 1d1d0f 1e1d0f 1f1e10 1e1e10 1f1f12
202013 202113 212214 242413 242413 242413 242412 242410 242611 272610
272612 262712 262710 282811 27290f 2a2b10 2b2c12 2c2d12 2e3012 303210
Keywords: Python, Numpy, OpenCV, parse, hex, hexadecimal, image, image processing, regex

PIL/Pillow convert Image to list and back again

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).
.

Categories