Fake-rivalry stimulus - psychopy

for a binocular rivalry experiment using color blobs (created with GratingStim using a gaussian mask), I need to draw a fake rivalry stimulus. That is, I need a round color blob that has one color for example on the top (25% of the color blob) and another color below (75% of the color blob). Additionally, I would like the twocolored fake rivalry blob to have a gaussian mask as my real rivalry stimuli do. Also it would be good to have a fuzzy color transition in the fake rivalry stimulus. I hope it's clear what I mean.
One solution I thought of was to draw two rectangles with blurred edges and then lay a gaussian alpha mask over them. In order to get the color proportions right, I would only have to move the two rectangles behind the mask. Is there a way to put a alpha-maks over an entire window?
Another solution would be to use ShapeStim as is suggested in this post explaining how to draw a semi circle : https://groups.google.com/forum/#!msg/psychopy-users/L9TYIrf9eJk/m0zIj0N23bMJ I would have to play around with the vertices, but I think it should work. The only thing that worries me here is that ShapeStim has no mask attribute to blur the edges.
Can you think of a way to do it?
Thank you very much!
Lilla
System specifications:
Psychopy v1.83.01 running on iOS 10.11.1

second update, even nicer result:
# Set up stimuli
from psychopy import visual, event
import numpy as np
from scipy.stats import gaussian_kde
win = visual.Window([500,500])
win2 = visual.Window([500,500])
#magic numpy stuff /scipy stuff, adapted from http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.stats.gaussian_kde.html
mean1 = [0, 0]
#the smaller the value, the bigger the visible blob
cov1 = [[0.03, 0], [0, 0.09]] #in this mask, it should be 50/50
cov2 = [[0.05,0],[0,0.4]] #in this mask, the color with this mask is the smaller one
m1, m2 = np.random.multivariate_normal(mean1, cov1, 2000).T# * np.random.multivariate_normal(mean2, cov2, 5000).T
for i in xrange(len(m2)):
if m2[i] >= 0:
m2[i] = m2[i]* 0.5#np.random.multivariate_normal(mean2, cov2,1).T[0]
values = np.vstack([m1, m2])
kernel = gaussian_kde(values)
xmin = m1.min()
xmax = m1.max()
ymin = m2.min()
ymax = m2.max()
X, Y = np.mgrid[xmin:xmax:128j, ymin:ymax:128j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([m1, m2])
kernel = gaussian_kde(values)
Z = np.reshape(kernel(positions).T, X.shape) #this array will be the mask
Z = Z - 1
for i in xrange(128):
for j in xrange(128): #it will neverbe smaller than -1
if Z[i][j] > 1:
Z[i][j] = 1
# Draw them on top of each other
perc75 = visual.GratingStim(win, sf=0, size=250, color='green',pos=(0.0, 0.0), mask =Z)
perc25 = visual.GratingStim(win, sf=0, size=250, color='red',pos=(0.0, 0.0), mask = 'raisedCos', maskParams={'fringeWidth':0.8})
perc25.setAutoDraw(True)
perc75.setAutoDraw(True)
win.flip()
event.waitKeys()

Would this work? You can then locate the blobs where you want, e.g. for one eye. No need to do whole-window stuff.
# Set up stimuli
from psychopy import visual, event
win = visual.Window([500,500])
blob_large = visual.GratingStim(win, sf=0, mask='gauss', size=1, color='red')
blob_small = visual.GratingStim(win, sf=0, mask='gauss', size=0.5, color='green', maskParams={'sd':6})
# Draw them on top of each other
blob_large.draw()
blob_small.draw()
win.flip()
# Wait for keyboard before quitting.
event.waitKeys()

Update on my question: the following exemplar code solves the problem:
# -*- coding: utf-8 -*-
# adapted from https://groups.google.com/forum/#!msg/psychopy-users/69p-aAWiDGI/e4iT43cHDeEJ
from psychopy import visual, event
win = visual.Window([500,500])
#stimuli
perc25 = visual.GratingStim(win, sf=0, size=1, color='RED',pos=(0.0, 0.0), mask = 'raisedCos', maskParams={'fringeWidth':0.8})
perc75 = visual.GratingStim(win, sf=0, size=0.8, color='green',pos=(0.0, -0.15), mask = 'raisedCos', maskParams={'fringeWidth':0.6})
#prepare for the screenshot
Stimlist = [perc25, perc75]
delta = .5# larger is bigger, slower
dx = delta * win.size[1]/win.size[0]
dy = delta
rect = (-dx, +dy, +dx, -dy)#size of the screenshot
screenshot = visual.BufferImageStim(win, stim=Stimlist,rect = rect, mask = 'gauss', pos=(0.0, 0.0)) # mask can also be 'raisedCos' with a smaller delta, for exmple .2
screenshot.draw()
win.flip()
event.waitKeys()

Related

using PyPlot in Julia 1.7: colorbar & equal 3D axis

I'm updating the syntax for julia 1.7. I have everything below working for julia 0.x, and below represents all I found for 1.7, but there are still glitches.
Question 1. How do I get the colorbar to match the colors on the torus? (And why is the cmap = "hot" not working?)
Question 2. Aspect "auto" works, but "equal" gives error "Axes3D currently only supports the aspect argument 'auto'", so how do I get axis equal aspect ratio?
using MAT
using PyPlot
using3D()
N = 200;
R = 10.0;
r = 0.3*R;
dx = 2*pi/(N);
y = zeros(N,1);
x = transpose(y) .+ 0.0;
for ix = 2:N; y[ix] = (ix-1)*dx; x[ix] = (ix-1)*dx; end
x = x .- pi;
y = y .- pi;
cosxsqr = cos.(x) .+ 0.0*y;
sinxsqr = sin.(x) .+ 0.0*y;
sinysqr = 0.0*x .+ sin.(y);
cosysqr = 0.0*x .+ cos.(y);
Rrcosxsqr = broadcast(+,r*cosxsqr,R);
rRrcosx = r*Rrcosxsqr[:];
Xsqr = Rrcosxsqr.*cosysqr;
Ysqr = Rrcosxsqr.*sinysqr;
Zsqr = r*sinxsqr;
display("maxZ")
display(maximum(Zsqr[:]))
display("minZ")
display(minimum(Zsqr[:]))
colors1 = (Zsqr.+abs(minimum(Zsqr[:])))./(2*maximum(Zsqr[:]))
display("min-color")
display(minimum(colors1[:]))
display("max-color")
display(maximum(colors1[:]))
colors3 = cat(colors1,colors1,colors1,dims=3)
pmesh = pcolormesh(colors1)
figure(1)
ax = subplot(1,1,1, projection="3d");
surf(Xsqr,Ysqr,Zsqr,facecolors=colors3,vmin=minimum(abs.(Zsqr[:])),vmax=maximum(abs.(Zsqr[:])),cmap="hot") # Q1 how to get correct colorbar
ax.set_aspect("auto") # Q2 how to get aspect equal
colorbar(pmesh)
Here is resulting pic.
Making my comment an answer since it became too long.
Some of Matplotlib's 3D plotting options aren't implemented as well as their 2D counterparts, but as a workaround you can change the aspect ratio of the containing box using ax.set_box_aspect((10, 10, 3)).
As for the colormap I'm not sure what you're after, your colorbar is referring to a plot that's not shown here. If you want it to be hot you can do the following:
figure(1)
ax = subplot(1,1,1, projection="3d");
s = surf(Xsqr,Ysqr,Zsqr,facecolors=colors3,vmin=minimum(abs.(Zsqr[:])),vmax=maximum(abs.(Zsqr[:])),cmap="hot")
ax.set_box_aspect((10, 10, 3))
colorbar(s, pad = 0.1)
Since you actually specify your own facecolors for the surf plot this doesn't really do anything, though. If you take out that named argument you get a 3D plot with the hot colormap:
figure(1)
ax = subplot(1,1,1, projection="3d");
s = surf(Xsqr,Ysqr,Zsqr,vmin=minimum(abs.(Zsqr[:])),vmax=maximum(abs.(Zsqr[:])),cmap="hot")
ax.set_box_aspect((10, 10, 3))
colorbar(s, pad = 0.1)
If you want your colorbar to have the same custom grayscale as your plot you need to create your own colormap (which is described well in the Matplotlib documentation)

How can I draw a rectangle from the points I want, using ROI?

Hello I am beginner in OpenCv.
I have a maze image. I wrote maze solver code. I need to get the photo like the picture for this code to work.
I want to choose the contours of the white area using ROI but I could not
When I try the ROI method I get a smooth rectangle with a black area selected.
https://i.stack.imgur.com/Ty5BX.png -----> this is my code result
https://i.stack.imgur.com/S7zuJ.png --------> I want to this result
import cv2
import numpy as np
#import image
image = cv2.imread('rt4.png')
#grayscaleqq
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
#cv2.imshow('gray', gray)
#qcv2.waitKey(0)
#binary
#ret,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV)
threshold = 150
thresh = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY)[1]
cv2.namedWindow('second', cv2.WINDOW_NORMAL)
cv2.imshow('second', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
#dilation
kernel = np.ones((1,1), np.uint8)
img_dilation = cv2.dilate(thresh, kernel, iterations=1)
cv2.namedWindow('dilated', cv2.WINDOW_NORMAL)
cv2.imshow('dilated', img_dilation)
cv2.waitKey(0)
cv2.destroyAllWindows()
#find contours
im2,ctrs, hier = cv2.findContours(img_dilation.copy(),
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#sort contours
sorted_ctrs = sorted(ctrs, key=lambda ctr: cv2.boundingRect(ctr)
[0])
list = []
for i, ctr in enumerate(sorted_ctrs):
# Get bounding box
x, y, w, h = cv2.boundingRect(ctr)
# Getting ROI
roi = image[y:y+h, x:x+w]
a = w-x
b = h-y
list.append((a,b,x,y,w,h))
# show ROI
#cv2.imshow('segment no:'+str(i),roi)
cv2.rectangle(image,(x,y),( x + w, y + h ),(0,255,0),2)
#cv2.waitKey(0)
if w > 15 and h > 15:
cv2.imwrite('home/Desktop/output/{}.png'.format(i), roi)
cv2.namedWindow('marked areas', cv2.WINDOW_NORMAL)
cv2.imshow('marked areas',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv2.dilate(dst,None)
image[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',image)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
list.sort()
print(list[len(list)-1])
I misunderstood your question earlier. So, I'm rewriting.
As #Silencer has already stated, you could use the drawContours method. You can do it as follows:
import cv2
import numpy as np
#import image
im = cv2.imread('Maze2.png')
gaus = cv2.GaussianBlur(im, (5, 5), 1)
# mask1 = cv2.dilate(gaus, np.ones((15, 15), np.uint8, 3))
mask2 = cv2.erode(gaus, np.ones((5, 5), np.uint8, 1))
imgray = cv2.cvtColor(mask2, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
maxArea1=0
maxI1=0
for i in range(len(contours)):
area = cv2.contourArea(contours[i])
epsilon = 0.01 * cv2.arcLength(contours[i], True)
approx = cv2.approxPolyDP(contours[i], epsilon, True)
if area > maxArea1 :
maxArea1 = area
print(maxArea1)
print(maxI1)
cv2.drawContours(im, contours, maxI1, (0,255,255), 3)
cv2.imshow("yay",im)
cv2.imshow("gray",imgray)
cv2.waitKey(0)
cv2.destroyAllWindows()
I used it on the following image:
And I got the right answer. You can add additional filters, or you could decrease the area using an ROI, to decrese the discrepancy, but it wasn't required
Hope it helps!
A simple solution to just draw a slanted rectangle would be to use cv2.polylines. Based on your result, I'm assuming you have the coordinates of the vertices of the area already, lets call them [x1,y1], [x2,y2], [x3,y3], [x4,y4]. The polylines function draws a line from vertex to vertex to create a closed polygon.
import cv2
import numpy as np
#List coordinates of vertices as an array
pts = np.array([[x1,y1],[x2,y2],[x3,y3],[x4,y4]], np.int32)
pts = pts.reshape((-1,1,2))
#Draw lines from vertex to vertex
cv2.polylines(image, [pts], True, (255,0,0))

About use tf.image.crop_and_resize

I'm working on the ROI pooling layer which work for fast-rcnn and I am used to use tensorflow. I found tf.image.crop_and_resize can act as the ROI pooling layer.
But I try many times and cannot get the result that I expected.Or did the true result is exactly what I got?
here is my code
import cv2
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
img_path = r'F:\IMG_0016.JPG'
img = cv2.imread(img_path)
img = img.reshape([1,580,580,3])
img = img.astype(np.float32)
#img = np.concatenate([img,img],axis=0)
img_ = tf.Variable(img) # img shape is [580,580,3]
boxes = tf.Variable([[100,100,300,300],[0.5,0.1,0.9,0.5]])
box_ind = tf.Variable([0,0])
crop_size = tf.Variable([100,100])
#b = tf.image.crop_and_resize(img,[[0.5,0.1,0.9,0.5]],[0],[50,50])
c = tf.image.crop_and_resize(img_,boxes,box_ind,crop_size)
sess = tf.Session()
sess.run(tf.global_variables_initializer())
a = c.eval(session=sess)
plt.imshow(a[0])
plt.imshow(a[1])
And I handed in my origin img and result:a0,a1
if I was wrong can anyone teach me how to use this function? thanks.
Actually, there's no problem with Tensorflow here.
From the doc of tf.image.crop_and_resize (emphasis is mine) :
boxes: A Tensor of type float32. A 2-D tensor of shape [num_boxes, 4].
The i-th row of the tensor specifies the coordinates of a box in the
box_ind[i] image and is specified in normalized coordinates [y1, x1,
y2, x2]. A normalized coordinate value of y is mapped to the image
coordinate at y * (image_height - 1), so as the [0, 1] interval of
normalized image height is mapped to [0, image_height - 1] in image
height coordinates. We do allow y1 > y2, in which case the sampled
crop is an up-down flipped version of the original image. The width
dimension is treated similarly. Normalized coordinates outside the [0,
1] range are allowed, in which case we use extrapolation_value to
extrapolate the input image values.
The boxes argument needs normalized coordinates. That's why you get a black box with your first set of coordinates [100,100,300,300] (not normalized, and no extrapolation value provided), and not with your second set [0.5,0.1,0.9,0.5].
However, as that why matplotlib show you gibberish on your second attempt, it's just because you're using the wrong datatype.
Quoting the matplotlib documentation of plt.imshow (emphasis is mine):
All values should be in the range [0 .. 1] for floats or [0 .. 255]
for integers. Out-of-range values will be clipped to these bounds.
As you're using float outside the [0,1] range, matplotlib is bounding your values to 1. That's why you get those colored pixels (either solid red, solid green or solid blue, or a mixing of these). Cast your array to uint_8 to get an image that make sense.
plt.imshow( a[1].astype(np.uint8))
Edit :
As requested, I will dive a bit more into
tf.image.crop_and_resize.
[When providing non normalized coordinates and no extrapolation values], why I just get a blank result?
Quoting the doc :
Normalized coordinates outside the [0, 1] range are allowed, in which
case we use extrapolation_value to extrapolate the input image values.
So, normalized coordinates outside [0,1] are allowed. But they still need to be normalized !
With your example, [100,100,300,300], the coordinates you provide makes the red square. Your original image is the little green dot in the upper left corner! The default value of the argument extrapolation_value is 0, so the values outside the frame of the original image are inferred as [0,0,0] hence the black.
But if your usecase needs another value, you can provide it. The pixels will take a RGB value of extrapolation_value%256 on each channel. This option is useful if the zone you need to crop is not fully included in you original images. (A possible usecase would be sliding windows for example).
It seems that tf.image.crop_and_resize expects pixel values in the range [0,1].
Changing your code to
test = tf.image.crop_and_resize(image=image_np_expanded/255., ...)
solved the problem for me.
Yet another variant is to use tf.central_crop function.
Below is a concrete implementation of the tf.image.crop_and_resize API. tf version 1.14
import tensorflow as tf
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import numpy as np
tf.enable_eager_execution()
def single_data_2(img_path):
img = tf.read_file(img_path)
img = tf.image.decode_bmp(img,channels=1)
img_4d = tf.expand_dims(img, axis=0)
processed_img = tf.image.crop_and_resize(img_4d,boxes=
[[0.4529,0.72,0.4664,0.7358]],crop_size=[64,64],box_ind=[0])
processed_img_2 = tf.squeeze(processed_img,0)
raw_img_3 = tf.squeeze(img_4d,0)
return raw_img_3, processed_img_2
def plot_two_image(raw,processed):
fig=plt.figure(figsize=(35,35))
raw_ = fig.add_subplot(1,2,1)
raw_.set_title('Raw Image')
raw_.imshow(raw,cmap='gray')
processed_ = fig.add_subplot(1,2,2)
processed_.set_title('Processed Image')
processed_.imshow(processed,cmap='gray')
img_path = 'D:/samples/your_bmp_image.bmp'
raw_img, process_img = single_data_2(img_path)
print(raw_img.dtype,process_img.dtype)
print(raw_img.shape,process_img.shape)
raw_img=tf.squeeze(raw_img,-1)
process_img=tf.squeeze(process_img,-1)
print(raw_img.dtype,process_img.dtype)
print(raw_img.shape,process_img.shape)
plot_two_image(raw_img,process_img)
Below is my working code, also output image is not black, this can be of help to someone
for idx in range(len(bboxes)):
if bscores[idx] >= Threshold:
#Region of Interest
y_min = int(bboxes[idx][0] * im_height)
x_min = int(bboxes[idx][1] * im_width)
y_max = int(bboxes[idx][2] * im_height)
x_max = int(bboxes[idx][3] * im_width)
class_label = category_index[int(bclasses[idx])]['name']
class_labels.append(class_label)
bbox.append([x_min, y_min, x_max, y_max, class_label, float(bscores[idx])])
#Crop Image - Working Code
cropped_image = tf.image.crop_to_bounding_box(image, y_min, x_min, y_max - y_min, x_max - x_min).numpy().astype(np.int32)
# encode_jpeg encodes a tensor of type uint8 to string
output_image = tf.image.encode_jpeg(cropped_image)
# decode_jpeg decodes the string tensor to a tensor of type uint8
#output_image = tf.image.decode_jpeg(output_image)
score = bscores[idx] * 100
file_name = tf.constant(OUTPUT_PATH+image_name[:-4]+'_'+str(idx)+'_'+class_label+'_'+str(round(score))+'%'+'_'+os.path.splitext(image_name)[1])
writefile = tf.io.write_file(file_name, output_image)

Marker size/alpha scaling with window size/zoom in plot/scatter

When exploring data sets with many points on an xy chart, I can adjust the alpha and/or marker size to give a good quick visual impression of where the points are most densely clustered. However when I zoom in or make the window bigger, the a different alpha and/or marker size is needed to give the same visual impression.
How can I have the alpha value and/or the marker size increase when I make the window bigger or zoom in on the data? I am thinking that if I double the window area I could double the marker size, and/or take the square root of the alpha; and the opposite for zooming.
Note that all points have the same size and alpha. Ideally the solution would work with plot(), but if it can only be done with scatter() that would be helpful also.
You can achieve what you want with matplotlib event handling. You have to catch zoom and resize events separately. It's a bit tricky to account for both at the same time, but not impossible. Below is an example with two subplots, a line plot on the left and a scatter plot on the right. Both zooming (factor) and resizing of the figure (fig_factor) re-scale the points according to the scaling factors in figure size and x- and y- limits. As there are two limits defined -- one for the x and one for the y direction, I used here the respective minima for the two factors. If you'd rather want to scale with the larger factors, change the min to max in both event functions.
from matplotlib import pyplot as plt
import numpy as np
fig, axes = plt.subplots(nrows=1, ncols = 2)
ax1,ax2 = axes
fig_width = fig.get_figwidth()
fig_height = fig.get_figheight()
fig_factor = 1.0
##saving some values
xlim = dict()
ylim = dict()
lines = dict()
line_sizes = dict()
paths = dict()
point_sizes = dict()
## a line plot
x1 = np.linspace(0,np.pi,30)
y1 = np.sin(x1)
lines[ax1] = ax1.plot(x1, y1, 'ro', markersize = 3, alpha = 0.8)
xlim[ax1] = ax1.get_xlim()
ylim[ax1] = ax1.get_ylim()
line_sizes[ax1] = [line.get_markersize() for line in lines[ax1]]
## a scatter plot
x2 = np.random.normal(1,1,30)
y2 = np.random.normal(1,1,30)
paths[ax2] = ax2.scatter(x2,y2, c = 'b', s = 20, alpha = 0.6)
point_sizes[ax2] = paths[ax2].get_sizes()
xlim[ax2] = ax2.get_xlim()
ylim[ax2] = ax2.get_ylim()
def on_resize(event):
global fig_factor
w = fig.get_figwidth()
h = fig.get_figheight()
fig_factor = min(w/fig_width,h/fig_height)
for ax in axes:
lim_change(ax)
def lim_change(ax):
lx = ax.get_xlim()
ly = ax.get_ylim()
factor = min(
(xlim[ax][1]-xlim[ax][0])/(lx[1]-lx[0]),
(ylim[ax][1]-ylim[ax][0])/(ly[1]-ly[0])
)
try:
for line,size in zip(lines[ax],line_sizes[ax]):
line.set_markersize(size*factor*fig_factor)
except KeyError:
pass
try:
paths[ax].set_sizes([s*factor*fig_factor for s in point_sizes[ax]])
except KeyError:
pass
fig.canvas.mpl_connect('resize_event', on_resize)
for ax in axes:
ax.callbacks.connect('xlim_changed', lim_change)
ax.callbacks.connect('ylim_changed', lim_change)
plt.show()
The code has been tested in Pyton 2.7 and 3.6 with matplotlib 2.1.1.
EDIT
Motivated by the comments below and this answer, I created another solution. The main idea here is to only use one type of event, namely draw_event. At first the plots did not update correctly upon zooming. Also ax.draw_artist() followed by a fig.canvas.draw_idle() like in the linked answer did not really solve the problem (however, this might be platform/backend specific). Instead I added an extra call to fig.canvas.draw() whenever the scaling changes (the if statement prevents infinite loops).
In addition, do avoid all the global variables, I wrapped everything into a class called MarkerUpdater. Each Axes instance can be registered separately to the MarkerUpdater instance, so you could also have several subplots in one figure, of which some are updated and some not. I also fixed another bug, where the points in the scatter plot scaled wrongly -- they should scale quadratic, not linear (see here).
Finally, as it was missing from the previous solution, I also added updating for the alpha value of the markers. This is not quite as straight forward as the marker size, because alpha values must not be larger than 1.0. For this reason, in my implementation the alpha value can only be decreased from the original value. Here I implemented it such that the alpha decreases when the figure size is decreased. Note that if no alpha value is provided to the plot command, the artist stores None as alpha value. In this case the automatic alpha tuning is off.
What should be updated in which Axes can be defined with the features keyword -- see below if __name__ == '__main__': for an example how to use MarkerUpdater.
EDIT 2
As pointed out by #ImportanceOfBeingErnest, there was a problem with infinite recursion with my answer when using the TkAgg backend, and apparently problems with the figure not refreshing properly upon zooming (which I couldn't verify, so probably that was implementation dependent). Removing the fig.canvas.draw() and adding ax.draw_artist(ax) within the loop over the Axes instances instead fixed this issue.
EDIT 3
I updated the code to fix an ongoing issue where figure is not updated properly upon a draw_event. The fix was taken from this answer, but modified to also work for several figures.
In terms of an explanation of how the factors are obtained, the MarkerUpdater instance contains a dict that stores for each Axes instance the figure dimensions and the limits of the axes at the time it is added with add_ax. Upon a draw_event, which is for instance triggered when the figure is resized or the user zooms in on the data, the new (current) values for figure size and axes limits are retrieved and a scaling factor is calculated (and stored) such that zooming in and increasing the figure size makes the markers bigger. Because x- and y-dimensions may change at different rates, I use min to pick one of the two calculated factors and always scale against the original size of the figure.
If you want your alpha to scale with a different function, you can easily change the lines that adjust the alpha value. For instance, if you want a power law instead of a linear decrease, you can write path.set_alpha(alpha*facA**n), where n is the power.
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if __name__ == '__main__':
my_updater = MarkerUpdater()
##setting up the figure
fig, axes = plt.subplots(nrows = 2, ncols =2)#, figsize=(1,1))
ax1,ax2,ax3,ax4 = axes.flatten()
## a line plot
x1 = np.linspace(0,np.pi,30)
y1 = np.sin(x1)
ax1.plot(x1, y1, 'ro', markersize = 10, alpha = 0.8)
ax3.plot(x1, y1, 'ro', markersize = 10, alpha = 1)
## a scatter plot
x2 = np.random.normal(1,1,30)
y2 = np.random.normal(1,1,30)
ax2.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
## scatter and line plot
ax4.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
ax4.plot([0,0.5,1],[0,0.5,1],'ro', markersize = 10) ##note: no alpha value!
##setting up the updater
my_updater.add_ax(ax1, ['size']) ##line plot, only marker size
my_updater.add_ax(ax2, ['size']) ##scatter plot, only marker size
my_updater.add_ax(ax3, ['alpha']) ##line plot, only alpha
my_updater.add_ax(ax4, ['size', 'alpha']) ##scatter plot, marker size and alpha
plt.show()

matplotlib tripcolor: mesh grid is showing up when i'm changing the alpha parameter

I'm trying to change the opacity of a tripcolor garph. Setting the alpha parameter is changing the opacity but is also showing up the mesh grid. I think that this is happening because the alpha parameter is not changing the opacity of the edges too. I tried to set edgecolor='none' but this is not solving my problem. Is there a way of changing the opacity without displaying the mesh grid?
"""
Pseudocolor plots of unstructured triangular grids.
"""
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
import math
# Creating a Triangulation without specifying the triangles results in the
# Delaunay triangulation of the points.
# First create the x and y coordinates of the points.
n_angles = 36
n_radii = 8
min_radius = 0.25
radii = np.linspace(min_radius, 0.95, n_radii)
angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
angles[:,1::2] += math.pi/n_angles
x = (radii*np.cos(angles)).flatten()
y = (radii*np.sin(angles)).flatten()
z = (np.cos(radii)*np.cos(angles*3.0)).flatten()
# Create the Triangulation; no triangles so Delaunay triangulation created.
triang = tri.Triangulation(x, y)
# Mask off unwanted triangles.
xmid = x[triang.triangles].mean(axis=1)
ymid = y[triang.triangles].mean(axis=1)
mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
triang.set_mask(mask)
# Illustrate Gouraud shading.
plt.figure()
plt.gca().set_aspect('equal')
plt.tripcolor(triang, z, shading='gouraud', cmap=plt.cm.rainbow, alpha=0.5, edgecolor='none')
plt.colorbar()
plt.title('tripcolor of Delaunay triangulation, gouraud shading')
plt.show()
Thank you very much for your time,
Dorin
you can try it with: " edgecolors='k', linewidth=0.0 " so this can set the linewidth to zero, which makes the line disapp