Updating Image with Matplotlib - python

i'm trying to update three of the plots in my subplot. I think the problem is that the old image isn't cleared, so the new one is plotted on top of the old one..but i`m not really sure..maybe there is also another problem. But it's possible to plot a new image with cv2.imshow()...problem is that i need to plot multiple images in a subplot. How can i fix it ?
Depending on the position of the slider the mask is changing.
Thanks a lot!
img.fig2, img.ax2 = plt.subplots()
for i in range(3):
plt.subplot(2, 3, i + 1)
plt.imshow(img[i])
plt.xticks([])
plt.yticks([])
def update (val):
...........
for i in range(3):
res[i] = cv2.cvtColor(res[i], cv2.COLOR_HSV2RGB)
fig2 = plt.figure(2)
fig2.add_subplot(2, 3, 4 + i)
plt.imshow(res[i])
plt.xticks([])
plt.yticks([])
plt.draw()
plt.show()
whole code of interest:
fig2, ax2 = plt.subplots()
for i in range(3):
plt.subplot(2, 3, i + 1)
plt.imshow(img[i])
plt.xticks([])
plt.yticks([])
plt.pause(0.1)
def update(val):
hsv1 = cv2.cvtColor(img1, cv2.COLOR_BGR2HSV)
hsv2 = cv2.cvtColor(img2, cv2.COLOR_BGR2HSV)
hsv3 = cv2.cvtColor(img3, cv2.COLOR_BGR2HSV)
l_b = np.array([shl.val, ssl.val, svl.val])
u_b = np.array([shu.val, ssu.val, svu.val])
mask1 = cv2.inRange(hsv1, l_b, u_b)
mask2 = cv2.inRange(hsv2, l_b, u_b)
mask3 = cv2.inRange(hsv3, l_b, u_b)
res1 = cv2.bitwise_and(img1, img1, mask=mask1)
res2 = cv2.bitwise_and(img2, img2, mask=mask2)
res3 = cv2.bitwise_and(img3, img3, mask=mask3)
res = [res1, res2, res3]
plt.clf()
for i in range(3):
res[i] = cv2.cvtColor(res[i], cv2.COLOR_HSV2RGB)
fig2 = plt.figure(2)
fig2.add_subplot(2, 3, 4 + i)
plt.imshow(res[i])
plt.xticks([])
plt.yticks([])
fig2.canvas.draw_idle()
plt.pause(0.01)
plt.draw_all()
shl.on_changed(update)
ssl.on_changed(update)
svl.on_changed(update)
shu.on_changed(update)
ssu.on_changed(update)
svu.on_changed(update)
plt.show()

You're right about imshow() : it adds new images on top of the existing ones and you need to call it only once before your loop. However, it also sets the color table and you'll need to provide it with a way to know the data range. Then you can call set_data() on the AxesImage imshow returned and you will need to call flush_events() to update the screen.
Sometimes flush_events() will happen transparently because another plot activates it, but you need to make sure it happens at least once.
Here is a working solution (link)

Related

Plotting border of masked array with plt.imshow

Actually plotting this image using plt.imshow and np.masked to plot the figure in blue. I would like to remove the inner part of the figure and just leaving the border. Tried with fill=False but that's not an arg for plt.imshow.
Code:
#img comes from a raster using GDAL
img = np.dstack((band1, band2, band3))
max = img.max()
img = img / max
one = f.add_subplot(1, 2, 1)
one.set_title('Polygon ID: '+index)
plt.imshow(img, cmap=plt.cm.binary)
plt.imshow(masked_polygon, 'jet', interpolation='none', alpha=0.3)
plt.axis('off')
Result of plot:

How do I superimpose an image in the back of a matplotlib plot?

I'm trying to superimpose an image in the back of a matplotlib plot. It is being rendered as HTML in a flask website so I am saving the plot as an image before inserting it. The plot without the background image looks like this:
The code that produces the above output is here:
fname = 'scatter_averages.png'
url_full = os.path.join(_path_full, fname)
image = plt.imread("app/static/images/quantum_grid.jpg")
if os.path.isfile(url_full):
os.remove(url_full)
plt.clf()
df = self.text_numeric_averages()
if df is not None:
plt.figure(figsize=(6, 8))
fig, ax = plt.subplots()
df.index += 1
x, y = df.iloc[:, 2], df.iloc[:, 1]
ax.plot(x, y, '-o')
plt.xlabel(df.columns[2])
plt.ylabel(df.columns[1])
for i in range(len(df)):
xyi = df.iloc[i, :].values
ax.annotate(str(df.index[i]) + " " + xyi[0][:3], (xyi[2], xyi[1]))
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
# ax.imshow(image, extent=[x_min, x_max, y_min, y_max])
plt.savefig(url_full)
The commented out line above is my attempt to get the image to superimpose. The output when that line is uncommented is this:
How do I keep the sizing and scale of the first image but use the background image in the second plot as the background? I'm not concerned with the image looking distorted.
ax.imshow(image, extent=[x_min, x_max, y_min, y_max], aspect="auto")
This will fix it.

Why is my annotation causing my figure size to change in matplotlib?

I am quite new to matplotlib and I am trying to do something simple, but I can't quite get the hang of it.
So I'm trying to piece together a couple of png images and label them A, B, C etc... but I want the labels to be in terms of figure fractions, so I can scale them in the future.
Here's my code for plotting the images:
im, h, w = loadImList(imList)
total_height = h[0] + h[1]
total_width = max(w[0],w[1])
new_img = np.ones(shape=(total_height, total_width, 3))
hStart[0] = 0
hStart[1] = h[0]
wStart[0] = 0
wStart[1] = 0
nIm = len(im)
for iDx in np.arange(len(im)):
new_img[np.int32(hStart[iDx]):np.int32(hStart[iDx]+h[iDx]),np.int32(wStart[iDx]):np.int32(wStart[iDx]+w[iDx])]=im[iDx]
fig_w = 4.7
fig_h = total_height / total_width * fig_w
fig, ax = plt.subplots(figsize=(fig_w, fig_h), constrained_layout=True)
fig.set_facecolor("white")
plt.imshow(new_img)
ax.set_xticks([])
ax.set_yticks([])
And this is what I get:
And then I tried to write a function that adds in the annotations:
def addTextbox(textstr, x, y, ax):
offsetbox = TextArea(textstr)
ab = AnnotationBbox(offsetbox, (x,y),
xybox=(x, y),
xycoords='figure fraction',
boxcoords="figure fraction",
box_alignment=(0, 0),
frameon=False,
pad=0,
arrowprops=None,
fontsize=36,
clip_on = False)
ax.add_artist(ab)
return ax
Adding ax = addTextbox("B)", 0.05, 0.5, ax) gives the image below which is reasonable.
But, when I do
ax = addTextbox("A)", 0.01, 0.6, ax)
ax = addTextbox("B)", 0.05, 0.5, ax)
I get a white margin on the left side of the image, and the B) annotation shifts. I don't understand why this is happening, or how do I solve it. Any help will be appreciated. Thanks!

Fast way of saving many images with matplotlib and for loop

I noticed that there is a big performance gap between the following two solutions to load and save images with matplotlib. Can anyone explain why, and what is the best (and fastest) way to save images in a python for loop?
Implementation 1:
create a figure outside the for loop, update what is displayed and then save.
fig, a = plt.subplots(1, 3, figsize=(30, 20)) # <--------
# list_of_fnames is just a list of file names
for k, fname in enumerate(list_of_fnames):
with Image.open(fname) as img:
x = np.array(img)
y = process_image_fn1(x)
z = process_image_fn2(x)
a[0].imshow(x)
a[1].imshow(y)
a[2].imshow(z)
output_filename = f'results_{k}.png'
plt.savefig(output_filename, dpi=320, format='png', transparent=False, bbox_inches='tight', pad_inches=0)
Implementation 2:
create a figure inside the for loop, save it, finally destroy it.
# list_of_fnames is just a list of file names
for k, fname in enumerate(list_of_fnames):
with Image.open(fname) as img:
x = np.array(img)
y = process_image_fn1(x)
z = process_image_fn2(x)
fig, a = plt.subplots(1, 3, figsize=(30, 20)) # <--------
a[0].imshow(x)
a[1].imshow(y)
a[2].imshow(z)
output_filename = f'results_{k}.png'
plt.savefig(output_filename, dpi=320, format='png', transparent=False, bbox_inches='tight', pad_inches=0)
plt.close() # <--------
The first option could be improved in a couple of ways.
removing the previously plotted AxesImages (from imshow), so that you don't keep increasing the number of plotted images on the axes
fig, a = plt.subplots(1, 3, figsize=(30, 20)) # <--------
# list_of_fnames is just a list of file names
for k, fname in enumerate(list_of_fnames):
for ax in a:
ax.images.pop()
with Image.open(fname) as img:
x = np.array(img)
y = process_image_fn1(x)
z = process_image_fn2(x)
a[0].imshow(x)
a[1].imshow(y)
a[2].imshow(z)
output_filename = f'results_{k}.png'
plt.savefig(output_filename, dpi=320, format='png', transparent=False, bbox_inches='tight', pad_inches=0)
alternatively, create the AxesImages once per axes, then rather than replot them each iteration, use .set_array() to change what is plotted on the AxesImage
fig, a = plt.subplots(1, 3, figsize=(30, 20)) # <--------
# list_of_fnames is just a list of file names
for k, fname in enumerate(list_of_fnames):
with Image.open(fname) as img:
x = np.array(img)
y = process_image_fn1(x)
z = process_image_fn2(x)
if k == 0:
im0 = a[0].imshow(x)
im1 = a[1].imshow(y)
im2 = a[2].imshow(z)
else:
im0.set_array(x)
im1.set_array(y)
im2.set_array(z)
output_filename = f'results_{k}.png'
plt.savefig(output_filename, dpi=320, format='png', transparent=False, bbox_inches='tight', pad_inches=0)

3D surface plot of colorspace in python

My computer setup is Mac Mojave 10.14.4.
I am new to Python so I am using Jupyter Lab so that I can understand what each part is producing so please can you respond similarly.
I want to produce a 3d surface plot of a digitally printed fabric sample with z-axis plotting the color space.
Here is the hardcopy file.
[]
Here is the 3dContour plot of the same test fabric
img = cv2.imread(testROYGBIVB.jpg) img - cv2.cvtColor(img,
cv2.COLOR_BGR2HSV)
plt.imshow(img)
img0 = img
img0.shape
(70, 90,3)
x, y, z = img0.T
x = np.linspace(0, 7, 70) #start, step, total
y = np.linspace(0, 9, 90)
X, Y = np.meshgrid(x, y)
Z = np.invert(z) #makes it easier to view
font = {'family': 'sans-serif',
'color': 'black',
'weight': 'normal',
'size': 16,
}
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 256, cmap='cubehelix_r')
ax.set_xlabel('x',fontdict=font)
ax.set_ylabel('y',fontdict=font)
ax.set_zlabel('z',fontdict=font); #RGB values
ax.set_title('Ultra Cotton',fontdict=font);
plt.tight_layout()
plt.savefig('UltaCotton.png')
ax.view_init(60, 35)
fig
[]
My question is this - the color space values of my plot are HSV.
I can split these values as seen below to create a scatter.
But I would like to maintain the rod structure from the contour but with the color of the rods matching the defined color space HSV as seen in the scatter.
I would like my contour plot and my scatter plot to have a hybrid baby.
FYI - the z values were inverted so that the top surface would be easily visible.
Can this be done? Thanks
flags = [i for i in dir(cv2) if i.startswith('COLOR_')]
len(flags)
258
flags[40]
'COLOR_BGR2RGB'
hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
pixel_colors = img.reshape((np.shape(img)[0]*np.shape(img)[1], 3))
norm = colors.Normalize(vmin=-1.,vmax=1.)
norm.autoscale(pixel_colors)
pixel_colors = norm(pixel_colors).tolist()
h, s, v = cv2.split(hsv_img)
fig = plt.figure()
axis = fig.add_subplot(1, 1, 1, projection="3d")
axis.scatter(h.flatten(), s.flatten(), v.flatten(), facecolors=pixel_colors, marker=".")
axis.set_xlabel("Hue")
axis.set_ylabel("Saturation")
axis.set_zlabel("Value")
plt.show()
]4
plt.tight_layout()
plt.savefig('filename.png')
axis.view_init(45, 35)
#ax.set_title('Ultra Cotton');
plt.tight_layout()
plt.savefig('filenameView.png')
fig
[]
Like the others, I'm confused by what you are trying to achieve.
Is this anything like what you had in mind?
img = plt.imread('Jb2Y5.jpg')
nx,ny,_ = img.shape
X, Y = np.meshgrid(np.linspace(0,ny,ny),np.linspace(0,nx,nx))
fig, (ax1, ax2, ax3) = plt.subplots(1,3,subplot_kw=dict(projection='3d'), figsize=(10,3))
ax1.plot_surface(X,Y, img[:,:,0], cmap="Reds", alpha=0.5)
ax1.set_title('RED')
ax2.plot_surface(X,Y, img[:,:,1], cmap='Greens', alpha=0.5)
ax2.set_title('GREEN')
ax3.plot_surface(X,Y, img[:,:,2], cmap='Blues', alpha=0.5)
ax3.set_title('BLUE')

Categories