Good evening all, I'm really struggling with my code. I've made a 1D spectrum from a fits file. I've extracted the numerical values for each point along the file, but there are vertical lines of overexposed pixel values. I want to replace all values above 3000 with 0. This is what I've done so far:
import astropy as ap
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from astropy.io import fits
from pathlib import Path
from astropy.nddata import CCDData
from ccdproc import ImageFileCollection
import ccdproc as ccdp
from os import listdir, walk
import astropy.units as u
# this function converts the class astropy.io.fits.hdulist.HDUList to a numpy array as ccd data
fitsfile = fits.open("img/HLXSpectrum.fits")
def spec(fitsfile):
specList = fits.open("img/HLXSpectrum.fits", include_path=True)
imgList = []
for img in specList:
ccd = CCDData(fitsfile[0].data, unit="adu")
HLX = ccdp.trim_image(ccd, fits_section="[:2050, 480:840]")
imgList.append(ccd)
fitsfile.close()
specImg = CCDData(ccd, unit="adu")
return specImg
specImg = spec(fitsfile)
skyarray1 = specImg[180:220, 50:2045]
spectrum1 = np.array(skyarray1)
skyarray2 = specImg[220:260, 50:2045]
spectrum2 = np.array(skyarray2)
skyarray3 = specImg[140:180, 50:2045]
spectrum3 = np.array(skyarray3)
spectrumA = spectrum2 - spectrum3
spectrum = spectrumA - spectrum1
flux = []
pixel = []
fix = np.where(spectrum > 3000, spectrum, 0)
for i in range(len(fix[1])): # cropped img in x dimension
flux.append(np.sum(skyarray1[:, i]))
pixel.append(i)
plt.figure(figsize=(20, 16), dpi=800)
plt.plot(pixel, flux, color="red")
fig1 = plt.gcf()
plt.show()
# fig1.savefig("flux.png", dpi=800)
but no matter what I do, the image stays the same, even though the values in the arrays change. Why?
The problem comes down to what you're plotting here:
fix = np.where(spectrum > 3000, spectrum, 0)
for i in range(len(fix[1])): # cropped img in x dimension
flux.append(np.sum(skyarray1[:, i]))
pixel.append(i)
plt.figure(figsize=(20, 16), dpi=800)
plt.plot(pixel, flux, color="red")
fig1 = plt.gcf()
plt.show()
You're plotting flux, which is taking values from skyarray1, which has not been modified. I think you want to replace it with fix like this:
for i in range(len(fix[1])): # cropped img in x dimension
flux.append(np.sum(fix[:, i]))
pixel.append(i)
Related
I have a gas map in type of numpy array that has value of (-1,unexplored area),(100,obstacle/wall) and (range of 20.0 until 30.0, gas concentration reading) like enter image description here
but when I display it using matplotlib i cannot specific the different color for wall(black), unexplored area(grey), and gas concentration (red contour)
the figure display by the matplotlib is enter image description here
this is the program that i use to display in matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
df = pd.read_excel("/content/drive/MyDrive/Japan Data/291,194/a 291,194 (11).xlsx")
array = df.to_numpy()
x =range(0,400)
y = range(0,399)
plt.contourf(x,y,array,cmap="Reds_r")
plt.colorbar(label= "Gas concentration")
`
how i can display the gas map using matplotlib as the first figure?
Sorry that my comment sounded simplistic. I had more trouble following it that I should have, and I apologize.
Anyway, I was having problems getting the colormaps to accept the discrete values -1 and 100. I could get it to map between 20 and 30 well, but not those edge cases. I ended up doing a brute-force method that worked, but is much less elegant. Hopefully someone more knowledgeable might provide a better way. What I did was translate your 2D array of values into a 3D array of RGBA values, then plot it with imshow. Here's the code:
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import pandas as pd
import numpy as np
df = pd.read_excel('./kernel.xlsx', header=None)
array = df.to_numpy()
fig, ax = plt.subplots()
x = range(0, 400)
y = range(0, 400)
gas_data = array[(array > -1) & (array < 100)]
gas_min = gas_data.min()
gas_max = gas_data.max()
rows, cols = array.shape
arr3d = np.zeros((rows, cols, 4))
norm = Normalize(vmin=gas_min, vmax=gas_max)
for row_i in range(rows):
for col_j in range(cols):
val = array[row_i, col_j]
if val == -1: # Unexplored
color = (1, 1, 1, 1) # white
elif val == 100: # Wall
color = (0, 0, 0, 1) # black
else:
color = plt.cm.Reds_r(norm(val))
arr3d[row_i, col_j, :] = color
# print(row_i+1, " of ", rows)
ax.imshow(np.flipud(arr3d))
Resulting plot:
In my project, I have many polygons to draw for each time step.
At each step, the number of polygons varies, thus it is difficult to keep Axes.patchs and translate them to make the animation.
I want to create animation with final figures (show after calling matplotlib.pyplot.show()), how to do this?
We take the sin curve as example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ims = []
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
z = np.cos(x)
for i in range(1,100):
tmpx = x[:i]
tmpy = y[:i]
tmpz = z[:i]
plt.plot(tmpx, tmpz)
im = plt.plot(tmpx, tmpy)
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=200)
ani.save('/home/test.gif', writer='imagemagick')
plt.show()
There are two curves: animated-sin-curve and static-cos-curve.
the sin-curve is kept as Line2D objects for each step
the cos-curve stay static for each step.
In this way, we show different Artist object for each step.
But I want to keep the rasterized Line2D figure for each step.
I find classes of AxesImage/FigureImage, but I don't know how to save the rasterized figure and make them work.
I tried to convert figure.canvas to AxesImage with following code :
def fig2AxesImage(fig):
import PIL.Image as Image
fig.canvas.draw()
w, h = fig.canvas.get_width_height()
buf = numpy.fromstring(fig.canvas.tostring_argb(), dtype=numpy.uint8)
buf.shape = (w, h, 4)
# canvas.tostring_argb give pixmap in ARGB mode. Roll the ALPHA channel to have it in RGBA mode
buf = numpy.roll(buf, 3, axis=2)
image = Image.frombytes("RGBA", (w, h), buf.tostring())
image = numpy.asarray(image)
return plt.imshow(image, animated=True)
but with this way, I have to clear canvas at start of next frame, which make the final animation a blank video. (but the .jpg figures I output for each step get the right content)
Does anyone have done this before that save rasterized canvas-figures of matplotlib.pyplot.figure() as a animation Vedio?
celluloid for python 2.7
''' copy from celluloid'''
# from typing import Dict, List # not supported by python 2.7. So comment it
from collections import defaultdict
from matplotlib.figure import Figure
from matplotlib.artist import Artist
from matplotlib.animation import ArtistAnimation
__version__ = '0.2.0'
class Camera:
def __init__(self, figure):
self.figure_ = figure
self.offsets_ = { k:defaultdict(int) \
for k in ['collections', 'patches', 'lines', 'texts', 'artists', 'images']
}
self.photos_ = []
def snap(self):
frame_artists = []
for i, axis in enumerate(self.figure_.axes):
if axis.legend_ is not None:
axis.add_artist(axis.legend_)
for name in self.offsets_:
new_artists = getattr(axis, name)[self.offsets_[name][i]:]
frame_artists += new_artists
self.offsets_[name][i] += len(new_artists)
self.photos_.append(frame_artists)
def animate(self):
return ArtistAnimation(self.figure_, self.photos_)
I am trying to rebuild an image that I previously decomposed with SVD. The image is this:
I successfully decomposed the image with this code:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
s an array of the singular values of the image. The more singular values I take, the more the reconstructed image is similar to the original one.
For example, if I take 20 singular values:
n = 20
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
plt.show()
I would like to fix the minumum number of singular values in order to get a good result: an image pretty similary to the original one. Moreover, I would like to see how much the result changes when I take a higher number of singular values. I tried with an animation without success:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig = plt.figure()
def update(i):
S = np.zeros(np.shape(img))
n = 20
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
ani = FuncAnimation(fig = fig, func = update, frames = 20, interval = 10)
plt.show()
If you plot the s singular values you can see a very steep decreasing curve, better if you use a log scale for the y axis:
plt.semilogy(s, 'k-')
As you can see, the first 50 singular values are the most important ones: almost everyone more that 1000. Values from the ~50th to the ~250th are an order of magnitude lower and their values decreases slowly: the slope of the curve is contained (remember the logarithmic y scale). That beeing said I would take the first 50 elements to rebulid your image.
Regarding the animation:
while the animation updates frame by frame, the counter i is increased by 1. In your code, you mistakenly use i to slice the s and define S; you should rename the counter.
Moreover, as animation goes on, you need to take an increasing number of singular values, this is set by n which you keep constant frame by frame. You need to update n at each loop, so you can use it as the counter.
Furthermore, you need the erase the previous plotted image, so you need to add a plt.gca().cla() at the beginning of the update function.
Check the code below for reference:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig, ax = plt.subplots(1, 2, figsize = (4, 4))
ax[0].imshow(img)
ax[0].axis('off')
ax[0].set_title('Original')
def init():
ax[1].cla()
ax[1].imshow(np.zeros(np.shape(img)))
ax[1].axis('off')
ax[1].set_title('Reconstructed\nn = 00')
def update(n):
ax[1].cla()
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
ax[1].imshow(recon_img)
ax[1].axis('off')
ax[1].set_title(f'Reconstructed\nn = {n:02}')
ani = FuncAnimation(fig = fig, func = update, frames = 50, init_func = init, interval = 10)
ani.save('ani.gif', writer = 'imagemagick')
plt.show()
which gives this animation:
As you can see, the first 50 elements are enough to rebuild you image pretty well. The rest of the elements adds some noise and changes a little the background.
I am trying to convert the color map of a contour generated from non-Python application. I tried using Matthias Bussonnier's code available here, but is unable to give me a full conversion. I tried to truncate the color map to give me a full conversion, but again does not give me a complete conversion.
MWE
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors as colors
import matplotlib.image as mpimg
from scipy.spatial import cKDTree
import matplotlib
import matplotlib.cm as mplcm
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
new_cmap = colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
cmap(np.linspace(minval, maxval, n)))
return new_cmap
cmap = plt.get_cmap('jet')
cmap = truncate_colormap(cmap, 0.1, 0.9)
img = mpimg.imread('./test.png')[:,:,:3]
##interact(sub=(0, 500), d=(0,1,0.05))
def convert(sub=256,d=0.1, cin=cmap, cout='viridis'):
viridis = plt.get_cmap(cout)
jet = plt.get_cmap(cin)
jet256 = colors.makeMappingArray(sub, jet)[:, :3]
K = cKDTree(jet256)
oshape = img.shape
img_data = img.reshape((-1,3))
res = K.query(img_data, distance_upper_bound=d)
indices = res[1]
l = len(jet256)
indices = indices.reshape(oshape[:2])
remapped = indices
indices.max()
mask = (indices == l)
remapped = remapped / (l-1)
mask = np.stack( [mask]*3, axis=-1)
blend = np.where(mask, img, viridis(remapped)[:,:,:3])
fig, ax = plt.subplots()
fig.set_figheight(10)
fig.set_figwidth(10)
ax.imshow(blend)
fig.savefig('viridize.pdf')
convert()
Input image
Output image
How do I get a complete conversion of the color map (jet in this case) to viridis with Python?
As commented, the solution from How I can specify how rainbow color scheme should be converted to grayscale
will work, but with some small modifications.
I.e. you need to apply your target colormap to the values optained from that solution and hence modify the resulting array size to be 3D.
The conditions for this to work are:
You know the colormap that the original image has been produced with (origin_cmap)
All colors in that image are either grey scale (axes, text etc.) or part of that origin_cmap. I.e. there should not be any other line plot or similar in addition in the figure.
The original colormap is unambiguous, i.e. does not contain the same color twice.
The full range of the original colormap has been used to create the input image and the full range of the target colormap will be aimed for. (This condition can be weakend though if needed, by specifying a different norm and/or range)
The following will hence "viridify" a given image.
import numpy as np
import matplotlib.colors
import matplotlib.pyplot as plt
image = plt.imread("https://i.stack.imgur.com/NyLq2.png")
def changecolormap(image, origin_cmap, target_cmap):
r = np.linspace(0,1, 256)
norm = matplotlib.colors.Normalize(0,1)
mapvals = origin_cmap(norm(r))[:,:3]
def get_value_from_cm(color):
color=matplotlib.colors.to_rgb(color)
#if color is already gray scale, dont change it
if np.std(color) < 0.1:
return color
#otherwise return value from colormap
distance = np.sum((mapvals - color)**2, axis=1)
return target_cmap(r[np.argmin(distance)])[:3]
newim = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
c = image[i,j,:3]
newim[i,j, :3] = get_value_from_cm(c)
return newim
fig, (ax,ax2) = plt.subplots(ncols=2)
ax.imshow(image)
ax2.imshow(changecolormap(image, plt.cm.jet, plt.cm.viridis))
ax.axis("off")
ax2.axis("off")
plt.show()
I am producing the probability distribution function of my variable, which is temperature:
and I am going to produce several plots with temperature PDF evolution.
For this reason, I would like to link the color of the plot (rainbow-style) with the value of the peak of the temperature distribution.
In this way, it is easy to associate the average value of the temperature just by looking at the color.
Here's the code I have written for producing plots of the PDF evolution:
from netCDF4 import Dataset
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import seaborn as sns
from scipy.stats import gaussian_kde
my_file = 'tas/tas.nc'
fh = Dataset(my_file, mode='r')
lons = (fh.variables['rlon'][:])
lats = (fh.variables['rlat'][:])
t = (fh.variables['tas'][:])-273
step = len(t[:,0,0])
t_units = fh.variables['tas'].units
fh.close()
len_lon = len(t[0,0,:])
len_lat = len(t[0,:,0])
len_tot = len_lat*len_lon
temperature = np.zeros(len_tot)
for i in range(step):
temperature=t[i,:,:]
temperature_array = temperature.ravel()
density = gaussian_kde(temperature_array)
xs = np.linspace(-80,50,200)
density.covariance_factor = lambda : .25
density._compute_covariance()
plt.title(str(1999+i))
plt.xlabel("Temperature (C)")
plt.ylabel("Frequency")
plt.plot(xs,density(xs))
plt.savefig('temp_'+str(i))
Because the question is lacking a working snippet, I had to come up with some sample data. This creates three datasets, where each one is colored with a specific color between blue (cold) and red (hot) according to their maximum value.
import matplotlib.pyplot as plt
import random
from colour import Color
nrange = 20
mydata1 = random.sample(range(nrange), 3)
mydata2 = random.sample(range(nrange), 3)
mydata3 = random.sample(range(nrange), 3)
colorlist = list(Color('blue').range_to(Color('red'), nrange))
# print(mydata1) print(mydata2) print(mydata3)
plt.plot(mydata1, color='{}'.format(colorlist[max(mydata1)]))
plt.plot(mydata2, color='{}'.format(colorlist[max(mydata2)]))
plt.plot(mydata3, color='{}'.format(colorlist[max(mydata3)]))
plt.show()