I have an MHA file and when I write
from medpy.io import load
image_data, image_header = load("HG/0001/VSD.Brain.XX.O.MR_Flair/VSD.Brain.XX.O.MR_Flair.684.mha")
print(image_data.shape)
I get a tuple (160, 216, 176). What do these dimensions represent (for reference these are brain tumor images from BRATS 2013)? Your help is appreciated.
Edit: on Jupyter for the slider to work I did
import matplotlib.pyplot as plt
from ipywidgets import interact
import numpy as np
%matplotlib inline
#interact(x=(0, image_data.shape[2]))
def update(x):
plt.imshow(np.flip(image_data[x].T, 0))
but of course your code probably works on other editors
According to the documentation, load(image) "Loads the image and returns a ndarray with the image’s pixel content as well as a header object."
Further down in medpy.io.load it says that image_data is "The image data as numpy array with order x,y,z,c.".
Edit: Because I was kind of curious to see what is actually in this file, I put together a quick script (heavily based on the slider demo) to take a look. I'll leave it here just in case it may be useful to someone. (Click on the "Layer" slider to select the z-coordinate to be drawn.)
from medpy.io import load
image_data, image_header = load("/tmp/VSD.Brain.XX.O.MR_Flair.684.mha")
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
axlayer = plt.axes([0.25, 0.1, 0.65, 0.03])
slider_layer = Slider(axlayer, 'Layer', 1, image_data.shape[2], valinit=1, valstep=1)
def update(val):
layer = slider_layer.val
ax.imshow(image_data[:,:,layer])
fig.canvas.draw_idle()
slider_layer.on_changed(update)
ax.imshow(image_data[:,:,0])
plt.show()
(This indirectly confirms that image_data holds a 3-D voxel image.)
Just to add on top the accepted answer, we can visualize the slices with subplots and animation too:
from medpy.io import load
image_data, image_header = load("VSD.Brain.XX.O.MR_Flair.684.mha")
image_data = image_data / image_data.max()
plt.figure(figsize=(20,32))
plt.gray()
plt.subplots_adjust(0,0,1,0.95,0.01,0.01)
for i in range(ct.shape[0]):
plt.subplot(16,10,i+1), plt.imshow(image_data[i]), plt.axis('off')
plt.suptitle('Brain-Tumor CT-scan mha (raw) files', size=15)
plt.show()
Related
I am using matplotlib to generate matrices I can train on. I need to get to the raw figure data.
Saving and reading the .png works fine, but my code runs 10x longer. Another stack overflow asked a similar question and the solution was to grab the canvas, but that related logic generated a numpy error. Here is my mwe.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import IdentityTransform
px = 1/plt.rcParams['figure.dpi'] # pixel in inches
fig, ax = plt.subplots(figsize=(384*px, 128*px))
i = 756
plt.text(70, 95, "value {:04d}".format(i), color="black", fontsize=30, transform=IdentityTransform())
plt.axis('off')
plt.savefig("xrtv.png") # I dont want to do this ...
rtv = plt.imread("xrtv.png") # or this, but I want access to what imread returns.
gray = lambda rgb: np.dot(rgb[..., :3], [0.299, 0.587, 0.114])
gray = gray(rtv)
Disabling rendering was a good hint. Consider using a memory buffer to I/O rather than playing with strings. Here is a full example:
import numpy as np
import io
import matplotlib.pyplot as plt
from PIL import Image
# disable rendering and dump to buffer
plt.ioff()
fig,ax = plt.subplots()
ax.plot(np.sin(np.arange(100)))
buf = io.BytesIO()
fig.savefig(buf,format='RGBA')
# plot from buffer
shape = (int(fig.bbox.bounds[-1]),int(fig.bbox.bounds[-2]),-1)
img_array = np.frombuffer(buf.getvalue(),dtype=np.uint8).reshape(shape)
Image.fromarray(img_array)
I'm trying to save a Matplotlib plot to an array using BytesIO as suggested here: Matplotlib save plot to NumPy array. Here is my code
import lightkurve
import matplotlib.pyplot as plt
import numpy as np
import io
def download(search):
lc = search.download() # downloads lightcurve as lightcurve object
if lc is not None:
fig,ax = plt.subplots()
ax.scatter(lc.time.value.tolist(), lc.flux.value.tolist(), color='k')
ax.autoscale()
ax.set_xlabel('Time (BTJD)')
ax.set_ylabel('Flux')
fig.show()
io_buf = io.BytesIO()
fig.savefig(io_buf,format="raw")
io_buf.seek(0)
img_arr = np.frombuffer(io_buf.getvalue(),dtype=np.uint8)
io_buf.close()
return img_arr
For some reason, the returned image array only contains the repeated value 255 like so: [255 255 255 ... 255 255 255] suggesting a blank image. I've tried using plt instead of fig, autoscaling the axes in case they weren't showing, and plotting instead with the Lightkurve built-in plotting function lc.plot(ax=ax) but nothing has changed. Does anyone know how to fix this?
I couldn't reproduce your bug. In fact, I ran your code (with some modifications) and the resulting image was exactly like the original image. Did you thoroughly check if your img_arr had only 255s? (e.g., np.unique(img_arr), in my case, len(np.unique(imgarr)) == 231)
import lightkurve
import matplotlib.pyplot as plt
import numpy as np
import io
def download(search):
lc = search.download() # downloads lightcurve as lightcurve object
if lc is not None:
fig,ax = plt.subplots()
ax.scatter(lc.time.value.tolist(), lc.flux.value.tolist(), color='k')
ax.autoscale()
ax.set_xlabel('Time (BTJD)')
ax.set_ylabel('Flux')
fig.show()
io_buf = io.BytesIO()
fig.savefig(io_buf,format="raw")
fig.savefig('test.png') # So I could see the dimensions of the array
io_buf.seek(0)
img_arr = np.frombuffer(io_buf.getvalue(),dtype=np.uint8)
io_buf.close()
return img_arr
# I put something random -- Next time, provide this step so others can more easily debug your code. Never touched lightkurve before
search = lightkurve.search_lightcurve('KIC 757076', author="Kepler", quarter=3)
imgarr = download(search)
fig, ax = plt.subplots()
ax.imshow(imgarr.reshape(288, -1), aspect=4, cmap='gray') # Visualizing the image from the array. Got '288' from the dimensions of the png.
Original plot:
Reconstructed plot:
I am trying to change content of an image interactively using a slider (e.g. for applying a threshold operation with different values).
My code is as follows:
#%matplotlib ipympl
%matplotlib widget
import matplotlib.pyplot as plt
import cv2
import numpy as np
import ipywidgets as widgets
from ipywidgets import HBox, IntSlider
from IPython.display import Image
def update_lines(change):
ret,thresh2 = cv2.threshold(img_gray,change.new,255,cv2.THRESH_BINARY)
plt.imshow(thresh2)
fig.canvas.flush_events()
image = cv2.imread("Untitled.jpg")
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret,thresh2 = cv2.threshold(img_gray,30,255,cv2.THRESH_BINARY)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
slider = IntSlider(
orientation='vertical',
step=1,
value=127,
min=0,
max=255
)
display(HBox([slider, fig.canvas]))
slider.observe(update_lines, names='value')
When executing my code, I have an unexpected behavior: the figure is displayed twice, the first time when I do fig = plt.figure() and the second time when I do display(HBox([slider, fig.canvas])) => see The figure is displayed twice.
How can I display the image only into the HBox ?
When I change the value with the slider, I have the following result => After changing value
It seems that matplotlib cannot directly be persuaded to plot the figure at the figure() call, but it's possible to encapsulate it in an Output widget (taken from here):
output = widgets.Output()
with output:
fig = plt.figure()
# fill figure with content here
display(HBox([slider, output]))
That way, the plot is correctly displayed once.
I want to make a plot using .fits files (astronomical images) and I am experiencing two issues which I think they are related:
Using this example from astropy:
from matplotlib import pyplot as plt
from astropy.io import fits
from astropy.wcs import WCS
from astropy.utils.data import download_file
fits_file = 'http://data.astropy.org/tutorials/FITS-images/HorseHead.fits'
image_file = download_file(fits_file, cache=True)
hdu = fits.open(image_file)[0]
wcs = WCS(hdu.header)
fig = plt.figure()
fig.add_subplot(111, projection=wcs)
plt.imshow(hdu.data, origin='lower', cmap='cubehelix')
plt.xlabel('RA')
plt.ylabel('Dec')
plt.show()
I can generate this image:
Now I would like to plot some points using the same coordinates as the image:
plt.scatter(85, -2, color='red')
However, when I do this:
I am ploting at the pixel coordinantes. Furthermore, the image no longer matches the frame size (although the coordinates seem fine)
Any advice on how to deal with these issues?
It is very easy to plot given coordinates. All you have to do is apply a transform.
I copied your example and added comments where I changed something and why.
from matplotlib import pyplot as plt
from astropy.io import fits
from astropy.wcs import WCS
from astropy.utils.data import download_file
fits_file = 'http://data.astropy.org/tutorials/FITS-images/HorseHead.fits'
image_file = download_file(fits_file, cache=True)
# Note that it's better to open the file with a context manager so no
# file handle is accidentally left open.
with fits.open(image_file) as hdus:
img = hdus[0].data
wcs = WCS(hdus[0].header)
fig = plt.figure()
# You need to "catch" the axes here so you have access to the transform-function.
ax = fig.add_subplot(111, projection=wcs)
plt.imshow(img, origin='lower', cmap='cubehelix')
plt.xlabel('RA')
plt.ylabel('Dec')
# Apply a transform-function:
plt.scatter(85, -2, color='red', transform=ax.get_transform('world'))
And the result is:
Note that if you want the Canvas to only show the region of the image just apply the limits again afterwards:
# Add a scatter point which is in the extend of the image:
plt.scatter(85.3, -2.5, color='red', transform=ax.get_transform('world'))
plt.ylim(0, img.shape[0])
plt.xlim(0, img.shape[1])
which gives:
A side note as well here. AstroPy has a great units support so instead of converting arcmins and arcsecs to degrees you can just define the "unit". You still need the transform though:
from astropy import units as u
x0 = 85 * u.degree + 20 * u.arcmin
y0 = -(2 * u.degree + 25 * u.arcmin)
plt.scatter(x0, y0, color='red', transform=ax.get_transform('world'))
I'm trying to learn opencv using python and came across this code below:
import cv2
import numpy as np
from matplotlib import pyplot as plt
BLUE = [255,0,0]
img1 = cv2.imread('opencv_logo.png')
replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)
plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')
plt.show()
source : http://docs.opencv.org/master/doc/py_tutorials/py_core/py_basic_ops/py_basic_ops.html#exercises
What does plt.imshow(img1, 'gray') do? I tried searching Google and all I could understand was that the 'gray' argument was a Color map. But my image (pic is there on the site. see link) is not displayed in grayscale. I tried removing the second argument. So the code was like plt.imshow(img1). It executes. The image remains same as before. Then what does the second argument 'gray' do? Can someone explain all this to me? Any help appreciated. Thanks.
PS. I'm totally new to Matplotlib
When img1 has shape (M,N,3) or (M,N,4), the values in img1 are interpreted as RGB or RGBA values. In this case the cmap is ignored. Per the help(plt.imshow) docstring:
cmap : ~matplotlib.colors.Colormap, optional, default: None
If None, default to rc image.cmap value. cmap is ignored when
X has RGB(A) information
However, if img were an array of shape (M,N), then the cmap controls the colormap used to display the values.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.axes_grid1 as axes_grid1
np.random.seed(1)
data = np.random.randn(10, 10)
fig = plt.figure()
grid = axes_grid1.AxesGrid(
fig, 111, nrows_ncols=(1, 2), axes_pad = 0.5, cbar_location = "right",
cbar_mode="each", cbar_size="15%", cbar_pad="5%",)
im0 = grid[0].imshow(data, cmap='gray', interpolation='nearest')
grid.cbar_axes[0].colorbar(im0)
im1 = grid[1].imshow(data, cmap='jet', interpolation='nearest')
grid.cbar_axes[1].colorbar(im1)
plt.savefig('/tmp/test.png', bbox_inches='tight', pad_inches=0.0, dpi=200,)