Make a heatmap whit 2d points and 2 images - python

Good day to all,
I am trying to create a heatmap, given a set of x, y coordinate pairs extracted from a CSV file. I am using numpy.histogram2d to plot it, the heatmap gives me correct, but I need to overlay it on another image. I share my code
Here is the CSV
csv = pd.read_csv("../test2.csv")
#Gets the centroid of the coords
csv["x"] = (combined_csv["x_min"]+(combined_csv["x_max"]-combined_csv["x_min"])/2).astype(int)
csv["y"] = (combined_csv["y_min"]+(combined_csv["y_max"]-combined_csv["y_min"])/2).astype(int)
am = csv[(csv["tracking_id"] != 7) &(csv["tracking_id"] != 28)] #Some filters
#Taken from another question
def myplot(x, y, s, bins=1000):
heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins, range=[[0, 960], [0, 480]])
heatmap = gaussian_filter(heatmap, sigma=s)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
return heatmap.T, extent
img, extent = myplot(am["x"], am["y_max"], 16,200)
plt.imshow(img, extent=extent, origin='upper', cmap=cm.jet)
The previous code gives me this image
I put the range between 0 and 480 for x and 0 and 960 for y, because the target image has that size, but doing this, histogram2d returns me the histogram matrix h, which is 200x200, then I use matplotlib to plot it and This graphs me correctly in the range 0-480 for x and 0-960 for y, what I need is to save said image, preserving as much information as possible in the 480x960 pixel size, and then add it with the target image using this tutorial how to superimpose heatmap on a base image?
final_img = cv2.addWeighted (heatmap_img, 0.5, image, 0.5, 0)
For creating something like this
But the image and the histogram doesnt match in the sizes that i wanted.
This is the target image

Ok, so i fix the error by the next code, printing the two images at the same plot
base_image = image
sigma = 8
fig, ax = plt.subplots(1, 1, figsize = (20, 16))
heatmap, xedges, yedges = np.histogram2d(am["x"], am["y"], bins=200, range=[[0,960],[0,480]])
heatmap = gaussian_filter(heatmap, sigma=sigma)
extent = [xedges[0], xedges[-1], yedges[-1], yedges[0]]
img = heatmap.T
ax.imshow(img, extent=extent, cmap=cm.jet)
ax.imshow(base_image, alpha = 0.5)
ax.set_xlim(0, 960)
ax.set_ylim(480,0)
ax.axes.get_yaxis().set_visible(False)
ax.axes.get_xaxis().set_visible(False)
plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.savefig("heatmap_final.png", bbox_inches='tight')
plt.show()
plt.close(fig)

Related

Plotting 2d histogram of data with very different ranges in Python

I try to plot a 2d histogram of data with very different ranges using the following code. However, because of the different data ranges, the x data overlaps like the following figure. Is there any solution that plots x and y data with the same axis length?
import numpy as np
from matplotlib import pyplot as plt
plt.clf()
x = np.random.randint(low=0, high=10, size=8873)
y = np.random.randint(low=100000,high=600000, size=8873)
heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()
Note that imshow() sets the aspect to 1 by default, changing it to auto should solve your problem. You can also calculate your own aspect based on extent to get for example a square image.
aspect = (extent[1] - extent[0]) / (extent[3] - extent[2])
plt.imshow(heatmap.T, extent=extent, origin='lower', aspect=aspect)
# plt.imshow(heatmap.T, extent=extent, origin='lower', aspect='auto')

Matplotlib: Plot images instead of points without overlapping images

Using this SO post, I can plot images instead of points:
def _implot(self, x, y, image, ax, zoom=1):
im = OffsetImage(image, zoom=zoom, cmap='gray_r')
ab = AnnotationBbox(im, (x, y), xycoords='data', frameon=False)
ax.add_artist(ab)
ax.update_datalim(np.column_stack([x, y]))
ax.autoscale()
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10, 10)
for img, x in zip(Y, X):
img = img.reshape(28, 28)
self._implot(x[0], x[1], img, ax=ax, zoom=0.5)
Above, Y is an Nx(28*28) matrix of flattened images and X is an Nx2 matrix of locations. My issue is that many of these images overlap, making the figure hard to read:
I'd like to skip plotting any image if it would overlap with an existing image. How can I do this? I could manually track which (x, y) coordinates have been plotted and also account for the image width and height, but that seems brittle, and I'm hoping Matplotlib has a built-in way to check for overlapping artists.

Over-plot an equation curve over a png image

enter image description hereI'm having trouble overplotting a relation between radial velocity and offset(position). I've looked at various solutions, but it doesn't seem to work. I've converted the equation into numbers, with only one variable.It also doesn't display the picture to the required dimensions.
x = np.linspace(-0.8 ,0.8 , 1000)
y = 0.5*((1.334e+20/x)**0.5)
img = plt.imread('Pictures/PVdiagram1casaviewer.png')
fig, ax = plt.subplots(figsize=(16, 16), tight_layout=True)
ax.set_xlabel('Offset(arcsec)', fontsize=14)
ax.set_ylabel('Radial Velocity (Km/S)', fontsize=14)
ax.imshow(img, extent=[-0.8, 0.8, -5, 15])
ax.plot(x, y, linewidth=5, color='white')
plt.title('PV Diagram')
plt.show()
enter image description here
If I plot your image, you can see that the axis of the image and matplotlib don't match, because the image contains space between the plot and border of the pictures (axis titles, and so on...)
So, first you need to crop the image, so that it contains just the plot area.
Next, you can plot the image with the argument aspect=auto to scale it to your figsize:
ax.imshow(img, extent=[-0.8,0.8,-5,15], aspect='auto')
If you try to plot your y function over the image, you will see that the values of y are much larger, so the curve is above the image (notice the tiny image is at the bottom).
I don't know what the physical background of y is, but if you divide it by 10e9 it fits inside the image-range.
Full code:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-0.8 ,0.8 , 1000)
y = 0.5*((1.334e+20/x)**0.5)/10e9 # Scale it here... but how?
img = plt.imread('hNMw82.png')
fig, ax = plt.subplots(figsize=(16, 16), tight_layout=True)
ax.set_xlabel('Offset(arcsec)', fontsize=14)
ax.set_ylabel('Radial Velocity (Km/S)', fontsize=14)
ax.imshow(img, extent=[-0.8,0.8,-5,15], aspect='auto')
ax.plot(x, y, linewidth=5, color='white')
ax.set_ylim([-5,15])
ax.set_xlim([-0.8,0.8])
plt.title('PV Diagram')
plt.show()
Result:
(I also set the axis limits.)

Change shape of Ellipse handle in legend

I'm trying to plot the labels of some contours and an ellipse in a single legend. I'm almost there (code below), but I'd like the shape associated to the ellipse in the legend to be a straight line, instead of a rectangle as it is by default.
How can I change this?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
# Random data
ndim, nsamples = 2, 1000
samples = np.random.randn(ndim * nsamples).reshape([nsamples, ndim])
fig, ax = plt.subplots()
x, y = samples.T
H, X, Y = plt.hist2d(x, y, bins=20, cmap=plt.get_cmap('Greys'))[:-1]
# Plot two contours
contour = ax.contour(
H.T, levels=(5, 10), extent=[x.min(), x.max(), y.min(), y.max()])
# Plot ellipse
ellipse = Ellipse(xy=(0., 0.), width=3., height=2, angle=45, edgecolor='r', fc='None', label='ellipse')
ax.add_patch(ellipse)
# Get ellipse's handle and label
ellip, ellip_lbl = ax.get_legend_handles_labels()
# Plot legend
plt.legend(ellip + list(reversed(contour.collections)), ellip_lbl + ['1s', '2s'])
plt.show()
Below is the solution based on this answer. The main idea here is to use ls="-", by plotting an empty list and grabbing its handle. Store the ellipse's patch in ax1 and use it to get the label.
ellipse = Ellipse(xy=(0., 0.), width=3., height=2, angle=45, edgecolor='r', fc='None', label='ellipse')
ax1 = ax.add_patch(ellipse)
# Get ellipse's handle and label
ellip, ellip_lbl = ax.get_legend_handles_labels()
plt.legend(handles = [plt.plot([],ls="-", color='r')[0]] + list(reversed(contour.collections)),
labels=[ax1.get_label()] + ['1s', '2s'])

Resizing imshow heatmap into a given image size in matplotlib

I'm using matplotlib for plotting, and I have the following matrix, Mat which I would like to plot into a heatmap.
Mat.shape
which yields (20,20).
I use the following code to plot it into a heatmap, following the this.
plt.imshow(Mat, cmap='Reds', interpolation='nearest')
plt.show()
But I have to resize this heatmap into 1600 x 1200(x,y) size, since, I want it to be overlapped with the image, img. The code is as follows.
plt.imshow(img, alpha=.5) # for image
plt.xlim(0, 1600)
plt.ylim(1200, 0)
plt.axis('off')
plt.imshow(Mat, cmap='Reds', interpolation='nearest', alpha=.5) # for heatmap to overlap
plt.show()
For some reason I would not like to change the size of img.
My try
I tried to resize at the level of plt.imshow(Mat), which I found hard. The only option I see at present is to resize Mat into 1600 x 1200 matrix with redundancy. Anyone to give me some efficient solution?
Use the extent= parameter of imshow to scale your matrix to the image's scale (more information here as well).
plt.figure()
img = plt.imread('stinkbug.png')
plt.imshow(img, alpha=1) # for image
#plt.axis('off')
xmin, xmax = plt.xlim()
ymin, ymax = plt.ylim()
Mat = np.random.normal(size=(20,20))
plt.imshow(Mat, cmap='Reds', interpolation='nearest', alpha=.5, extent=(xmin,xmax,ymin,ymax)) # for heatmap to overlap
plt.show()

Categories

Resources