Loading single images from large tiff stack in Python - python

I'm working in Python on image stacks with up to 1000 images in tiff format. Each image is a 16bit image, 2048x2048 pixels, full size of 8MB, thus the full stack is several GB large. I have three color channels which I merge. The operations I do on the merged stacks (like converting to HSV space, applying filters...) I do on each individual image of the stack.
But when I want to load the full stacks in the beginning, I obviously run out of memory.
Is there a way to handle these large stacks? I had the idea to only load the single images from the stack, do the operations on that individual image, and concatenate it to a stack again. Unfortunately, I couldn't find a way to do that. Or is there a better solution?
Any help is very much appreciated.
Here is my code so far (where I run out of memory when loading the images, but it works with smaller test stacks):
import numpy as np
from skimage import io
import matplotlib
b = io.imread('blue_channel.tif')
g = io.imread('green_channel.tif')
r = io.imread('red_channel.tif')
Npic = np.shape(b)[0] #get number of images
sizex = np.shape(b)[1] #get size in x
sizey = np.shape(b)[2] #get size in y
Ncol = 3
merged_channels = np.zeros([Npic, sizex, sizey, Ncol])
merged_channels[:,:,:,0] = r
merged_channels[:,:,:,1] = g
merged_channels[:,:,:,2] = b
merged_channels = merged_channels.astype(np.float32) #Cast Image data type
merged_channels /= 255 #Scale value to float32 range 0-1
merged_channels_hsv = np.zeros([Npic, sizex, sizey, Ncol])
for i in tqdm(range(Npic)):
merged_channels_hsv[i, :, :, :] = matplotlib.colors.rgb_to_hsv(merged_channels[i, :, :, :])
merged_channels_hsv *= 255
merged_channels_hsv = merged_channels_hsv.astype(np.uint8)
imsave('merged_channels_hsv.tif', merged_channels_hsv[:,:,:,:])
Here are sample images:
Merged RGB image:
Converted to HSV space, only H-channel:
EDIT:
I found a way to define the image numbers I want to load from a tiff-file using
skimage.external.tifffile.imread(files, **kwargs):
import numpy as np
from tifffile import imread
def sequence(start, end):
res = []
diff = 1
x = start
while x <= end:
res.append(x)
x += diff
return res
seq = sequence(1, 100) #load first 100 images from stack
merged_channels = imread('filename.tif', key = seq)

Related

Need help converting a .wav audio file to a .PNG , then back to the same .wav file

I am attempting to convert a sound file into an image, then back into that same sound file in Python. First, I'm reading the .wav with python's wave library, extract the frames, and then arrange the bytes as RGB tuples in a square image.
The output is cool and looks like this
but when I try to convert the image back to a soundfile, the result is horrid. Not sure what I'm doing wrong here
import wave
from PIL import Image
import numpy as np
from math import sqrt
w = wave.open("sample.wav", mode = "rb")
frames = w.readframes(w.getnframes())
pixels = []
#####FRAMES CONVERTED TO PIXEL TUPLES######
for i in range(0,w.getnframes(),3):
pixels.append((frames[i],frames[i+1],frames[i+2]))
#####FIT TO SQUARE IMAGE#####
dimensions = int(sqrt(w.getnframes()/3))
img = []
for x in range(0,dimensions):
row = []
for y in range(0,dimensions):
row.append(pixels[x*dimensions+y])
img.append(row)
array = np.array(img, dtype=np.uint8)
new_image = Image.fromarray(array)
new_image.save('new.png')
p = Image.open("new.png",mode="r")
flatten = [x for sets in list(p.getdata()) for x in sets]
###### WAV RE-CREATION ######
sampleRate = w.getframerate() # hertz
obj = wave.open('sound.wav','w')
obj.setnchannels(w.getnchannels())
obj.setsampwidth(2)
obj.setframerate(sampleRate)
for i in range(0,len(flatten)):
obj.writeframesraw(( flatten[i]).to_bytes(8,"big") )
obj.close()
You are introducing loss in your conversion to pixels.
First, you will lose one or two frames at the end with for i in range(0,w.getnframes(),3):, when the number of frames is not a multiple of three.
Second, your dimensions = int(sqrt(w.getnframes()/3)) and then writing dimensions squared pixels will lose many frames when the number of frames divided by three is not a square.
Third, and most importantly, you are ignoring the sample width, as well as the number of channels. You are only saving the low eight bits of each sample in the image. If the sample width is 16 bits, you are essentially saving noise in the image.

How to combine splitted images with overlap

I have a code that split large images into 1024X1024 small images with 10% overlap. After this process I am processing each 1024X1024 small image. Finally, I want to combine these small images I have processed in the original image size. How can I do the combine process? Can you share some sample code? Thanks...
import cv2
path_to_img = "demo.png"
img = cv2.imread(path_to_img)
img_h, img_w, _ = img.shape
split_width = 1024
split_height = 1024
def start_points(size, split_size, overlap=0):
points = [0]
stride = int(split_size * (1-overlap))
counter = 1
while True:
pt = stride * counter
if pt + split_size >= size:
points.append(size - split_size)
break
else:
points.append(pt)
counter += 1
return points
X_points = start_points(img_w, split_width, 0.1)
Y_points = start_points(img_h, split_height, 0.1)
splitted_images = []
for i in Y_points:
for j in X_points:
split = img[i:i+split_height, j:j+split_width]
splitted_images.append(split)
To reconstruct the original image is almost the same principle. Simply use the horizontal and vertical coordinates you created and reverse the operation. You will of course need to use an external counter that will help you iterate through your list of patches. The only other intricacy you need is to declare a container that will house the final image. You can do that by declaring an array of the same type as the input image and setting it to all zeroes:
import numpy as np
final_image = np.zeros_like(img)
index = 0
for i in Y_points:
for j in X_points:
final_image[i:i+split_height, j:j+split_width] = splitted_images[index]
index += 1
final_image will now contain the reconstructed image using the patches. Take note that I have simply overwritten any values that are overlapping with the most recent patch that overlaps any area of interest that would have overlapping values last.

Axis out of bounds error in Python using PIL and OpenCV 2

I wanted to change all the pixels in an image to a grey color (r = g = b = 128) if they are in a certain threshold (if the value is between 50 and 150 change it). I imported the image and when i try to process the image it gives me the following error : IndexError: index 3474 is out of bounds for axis 0 with size 3474 (the image is 3474x4632).
Here's the code:
from PIL import Image
import numpy as np
image = Image.open("texture.jpg")
w, h = image.size
print ("%d %d" % (w, h)) #to be sure what the width and height are
im = np.array(image)
for x in range(0, w):
for y in range(0, h):
if (im[x][y][0] <= 150 and im[x][y][0] >= 50):
im[x][y][0] = 128
im[x][y][1] = 128
im[x][y][2] = 128
cv2.imwrite("image2.jpg", im)
And here's the image i'm trying to convert: https://ibb.co/hnjq4p (too large to upload here). Any ideas about why it doesn't work ?
I believe that numpy reverses the axis order from PIL. Actually the first index is rows. So you should loop through w,h = im.shape or h,w = image.size instead. Maybe you can verify that this is correct by comparing image.size and im.shape?
That said, it will be much better if you do not loop. You can use masking and broadcasting to achieve the for loop task like this:
im[(im[...,0]<=150)&(im[...,0]>=50)] = 128 # will modify im in place
This will be much faster especially on large images like this.
Note that this only checks the first channel of the image to be between 150 and 50. This is what your for loop says so I guess it's what you want.
Please check im.shape: you should index your pixels as im[y,x] after converting to a numpy.array.

Speed up image manipulation calculation with numpy

I have a very simple method that converts an RGB image to HSL and adjusts the Hue. This works relatively quickly with small image files, but large image files require minutes to process. I am converting the imgdata to a numpy array, but this does not seem to speed it up at all. Do I have to use numpy functions exclusively inside of the loop to speed this up? I can't find exactly where the bottleneck is inside of the loop as it is just fairly simple math calculations.
from colorsys import rgb_to_hls, hls_to_rgb
from numpy import array
def reload_img():
global img, sizew, sizeh, maxsize, imgdata
img = Image.open(IMAGE_SRC)
sizew, sizeh = img.size
maxsize = ((sizew/2)**2 + (sizeh/2)**2)**0.5
imgdata = list(img.getdata())
# Convert to numpy array
imgdata = array(imgdata)
IMAGE_SRC = "test.jpg"
reload_img()
# Adjust Hue
for i in range(0,len(imgdata)):
r,g,b = imgdata[i]
r /= 255.0
g /= 255.0
b /= 255.0
(h, l, s) = rgb_to_hls(r,g,b)
h = .50
imgdata[i] = hls2rgb((h,l,s))
Here's a fast but not super precise method:
import numpy as np
from PIL import Image
def set_hue(img, hue):
"""
img - a PIL (pillow) image
hue - an integer in the range [0, 255]
Returns a new PIL (pillow) image in HSV mode
"""
hsv = img.convert('HSV')
hsv_ar = np.array(hsv)
hsv_ar[...,0] = hue
out = Image.fromarray(hsv_ar, mode='HSV')
return out
For this to work a somewhat recent version of Pillow (PIL fork) is probably required. It's fast because it uses the buffer protocol to convert between PIL format and Numpy array and vice versa. But the precision of the hue modification is not perfect, because there's an intermediate result with only 24 bits per pixel.
Note that Pillow doesn't have a HSL mode, so I used HSV.

How can I improve this disparity map?

I have been working on a piece of code to create a disparity map.
I don't want to use OpenCV for more than loading / saving the images converting them to grayscale.
So far, I've managed to implement the algorithm explained in this website. I'm using the version of the algorithm that uses the Sum of Absolute Differences (SAD). To test my implementation, I'm using the stereo images from this dataset.
Here's my code:
import cv2
import numpy as np
# Load the stereo images
img = cv2.imread('bow-view1.png')
img2 = cv2.imread('bow-view5.png')
# convert stereo images to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
# get the size of the images
# l -> lines
# c -> columns
# v -> channel (RGB)
l,c,v = img.shape
# initialize arrays
minSAD = np.ones((l,c)) * 1000
sad = np.ones((l,c))
winsad = np.ones((l,c))
disp = np.zeros((l,c))
max_shift = 30
# set size of the SAD window
w_l = 2
w_c = 2
for shift in range(max_shift):
print("New Shift: %d"%(shift))
for u in range(0,l):
for v in range(0,c):
# calculate SAD
if(u+shift < l):
sad[u,v] = np.abs((int(gray[u,v]) - int(gray2[u+shift,v])))
sum_sad = 0
for d in range(w_l):
for e in range(w_c):
if(u+d < l and v+e < c):
sum_sad += sad[u+d,v+e]
winsad[u,v] = sum_sad
# Save disparity
if(sad[u,v] < minSAD[u,v]):
minSAD[u,v] = winsad[u,v]
disp[u,v] = shift
print("Process Complete")
# write disparity map to image
cv2.imwrite('outputHT/disparity/sad.png',disp)
print("Disparity Map Generated")
This is the output generated by that code:
I should get an output similar (or very close to) this:
I've tried several window sizes (in the SAD step), but I keep getting results like this one or images that are all black.
Any answer that helps me figure out the problem or that at least points me in the right direction will be very appreciated!
One thing you are missing here is that all the values in the disp array will be between 0 and 30 which correspond to black pixel, so in order to map these values between 0 and 255 you have to multiply the shift by 8.

Categories