I am a new Python user and would like to do some simple image processing. Essentially I will have a dynamic medical image - a series of 2D images at different time points which I would like to store as a 3D array. Due to the nature of the scanning technique there is likely to be occasional patient motion during certain imaging frames which makes the data unusable. I would like to delete such frames and recast the array - new dimensions (n-1, 256, 256). After deletion of the frame I would like to update the image display. What is the best way to achieve this goal? Here is the skeleton code I have so far:
import dicom
import numpy as np
import pylab
from matplotlib.widgets import Slider, Button
ds = dicom.read_file("/home/moadeep/Dropbox/FS1.dcm")
#data = ds.pixel_array
data = np.random.rand(16,256,256)
nframes = data.shape[0]
ax = pylab.subplot(111)
pylab.subplots_adjust(left=0.25, bottom=0.25)
frame = 0
l = pylab.imshow(data[frame,:,:]) #shows 1024x256 imagge, i.e. 0th frame*
axcolor = 'lightgoldenrodyellow'
axframe = pylab.axes([0.35, 0.1, 0.5, 0.03], axisbg=axcolor)
#add slider to scroll image frames
sframe = Slider(axframe, 'Frame', 0, nframes, valinit=0,valfmt='%1d'+'/'+str(nframes))
ax_delete = pylab.axes([0.8,0.025,0.1,0.04], axisbg=axcolor)
#add slider to scroll image frames
#Delete button to delete frame from data set
bDelete = Button(ax_delete, 'Delete')
def update(val):
frame = np.around(sframe.val)
pylab.subplot(111)
pylab.subplots_adjust(left=0.25, bottom=0.25)
pylab.imshow(data[frame,:,:])
sframe.on_changed(update)
pylab.gray()
pylab.show()
The short answer to your question is use numpy.delete. E.g.
import numpy as np
data = np.arange(1000).reshape((10,10,10))
# Delete the third slice along the first axis
# (note that you can delete multiple slices at once)
data = np.delete(data, [2], axis=0)
print data.shape
However, this is a poor approach if you're going to be removing individual slices many times.
The longer answer is to avoid doing this each time you want to delete a slice.
Numpy arrays have to be contiguous in memory. Therefore, this will make a new copy (and delete the old) each time. This will be relatively slow, and requires you to have twice the free memory space required to store the array.
In your case, why not store a python list of 2D arrays? That way you can pop the slices you don't want out without any problems. If you need it as a 3D array afterwards, just use numpy.dstack to create it.
Of course, if you need to do 3D processing, you'll need the 3D array. Therefore, another approach would be to store a list of "bad" indicies and remove them at the end using numpy.delete (note that the items to be deleted is a list, so you can just pass in your list of "bad" indicies).
On a side note, the way you're updating the image will be very slow.
You're creating lots of images, so each one will be redrawn each time and the update will become very slow as you go on.
You're better off setting the data of the image (im.set_data(next_slice)) instead of creating a new image each time.
Better yet, use blitting, but with image data in matplotlib, it's not as advantageous as it is for other types of plots due to matplotlib's slow-ish rescaling of images.
As a quick example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
def main():
# Set up 3D coordinates from -10 to 10 over a 200x100x100 "open" grid
x, y, z = np.ogrid[-10:10:200j, -10:10:100j, -10:10:100j]
# Generate a cube of interesting data
data= np.sin(x*y*z) / (x*y*z)
# Visualize it
viewer = VolumeViewer(data)
viewer.show()
class VolumeViewer(object):
def __init__(self, data):
self.data = data
self.nframes = self.data.shape[0]
# Setup the axes.
self.fig, self.ax = plt.subplots()
self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
# Make the slider
self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes,
valinit=1, valfmt='%1d/{}'.format(self.nframes))
self.slider.on_changed(self.update)
# Plot the first slice of the image
self.im = self.ax.imshow(data[0,:,:])
def update(self, value):
frame = int(np.round(value - 1))
# Update the image data
dat = self.data[frame,:,:]
self.im.set_data(dat)
# Reset the image scaling bounds (this may not be necessary for you)
self.im.set_clim([dat.min(), dat.max()])
# Redraw the plot
self.fig.canvas.draw()
def show(self):
plt.show()
if __name__ == '__main__':
main()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With