I have created a 3d scatter plot that changes colour based around a separate variable, Intensity. Ideally I would change the opacity so that the lower intensity colours are less visible. To do this I created a separate function that should return a different value, to be placed as the alpha value, for each intensity value based upon its size compared to the max intensity value. When I run this though all scatter points receive the first opacity value I put into my function.
I can not spot the error and would appreciate if someone could cast there eye over it.
Section of code that generates plot and opacity function:
'''
Trial for opacity differentiation
'''
def OP(b):
for i in range(len(b)):
Imx = np.amax(b)
print(Imx)
if b[i] > .9*Imx:
return .9
elif b[i] <= .9*Imx:
return 0.1
else:
return 0
'''
3d Colour scatterplot of Intensity
'''
def hlkplt(filename):
h = np.linspace(0,4,9)
l = np.linspace(0,4,9)
k = np.linspace(0,4,9)
I = []
for j in range(len(h)):
for i in range(len(l)):
for n in range(len(k)):
IStot = Int2(filename,h[j],l[i],k[n])
p = IStot.real
I.append(p)
b = np.array(I)
hh, ll, kk = np.meshgrid(h,l,k)
cm = plt.get_cmap('RdYlGn')
fig = plt.figure()
ax3D = plt.axes(projection = '3d')
ax3D.set_xlabel('h plane')
ax3D.set_ylabel('l plane')
ax3D.set_zlabel('k plane')
p3d = ax3D.scatter(hh,ll,kk, s = 30, c = b,alpha =OP(b), marker ='o',label = filename)
plt.legend()
cbar = plt.colorbar(p3d)
cbar.set_label('Scattering Intensity of neutrons')
plt.show()
return
The Int2 function just produces a complex number which is turned real and placed into a list
Related
I'm working on an animation of a moving object, while drawing it's path.
I want to draw the pixels in which the center of the object went through... but guess what? python decided to set the NW anchor of the image with the coordinates I send, instead of the center. I infer it has something to do with the pixels I draw simultaneously (creating a one pixel rectangle). so the image appear on the right of the path bellow... I want the center of it to be on the top of the pixels... adding the main of the code:
from tkinter import*
import time
dt = 0.01
clock_place = (500, 10)
def round_two(t, t0):
return round((t-t0)*100)/100
def round_three(t, t0):
return round((t-t0)*1000)/1000
# showing 'real time motion' for a known path (also cyclic), with
# parametric representation
def paint_known_path(x_pos, y_pos, t_0):
window = Tk()
canvas = Canvas(window, height=700, width=1000)
canvas.pack()
canvas.config(background='black')
tennis_ball = PhotoImage(file='tennis ball.png')
t = t_0
x = x_pos(t_0)
y = y_pos(t_0)
particle = canvas.create_image(x, y, image=tennis_ball)
clock = canvas.create_text(clock_place, text=round_two(t, t_0),
fill='white')
while True:
canvas.create_rectangle(x, y, x, y, outline='red')
canvas.itemconfig(clock, text=round_two(t, t_0))
t += dt
x = x_pos(t)
y = y_pos(t)
canvas.moveto(particle, x, y)
window.update()
if x == x_pos(t_0) and y == y_pos(t_0):
if t - t_0 > 100*dt:
break
time.sleep(dt)
canvas.create_text((500, 100), text='orbit duration: ' +
str(round_three(t, t_0)), fill='white')
window.mainloop()
It turns out to be quite a bit require, but here is the main completion components.
The first additional part that you need to add:
# print('the ten ball height', tennis_ball.height(), tennis_ball.width())
# tennis ball dimensions
tb_hght = tennis_ball.height()
tb_wdth = tennis_ball.width()
mid_point_x = x + tennis_ball.height() / 2
mid_point_y = y + tennis_ball.width() / 2
Secondly, also needed to add some functions to for x_pos and y_pos like this (these are just example functions to make the code work):
def x_pos(a):
# any function of t,
return 100
def y_pos(a):
# any function of t,
return 100
Furthermore, you need to call the function at the end like this:
paint_known_path(x_pos,y_pos,0)
Finally, need to add the mid_point_x and mid_point_y to the path that is drawn (as these will be the image centre points).
So I was playing with animating some Bezier curves - just part of learning how to use ipycanvas (0,10,2) -- The animation I produced is really hurting my head. What I expected to see was a set of straight lines between 4 Bezier control points "bouncing" around the canvas with the Bezier curve moving along with them.
I did get the moving Bezier curve -- BUT the control points stayed static. Even stranger they were static in the final position and the curve came to meet them.
Now sometimes Python's structures and references can get a little tricky and so you can sometimes get confusing results if you are not really thinking it through -- and this totally could be what's going on - but I am at a loss.
So to make sure I was not confused I printed the control points (pts) at the beginning and then displayed them to the canvas. This confirmed my suspicion. Through quantum tunneling or some other magic time travel the line canvas.stroke_lines(pts) reaches into the future and grabs the pts array as it will exist in the future and keeps the control points in their final state.
Every other use of pts uses the current temporal state.
So what I need to know is A) The laws of physics are safe and I am just too dumb to understand my own code. B) There is some odd bug in ipycanvas that I should report. C) How to monetize this time-traveling code -- like, could we use it to somehow factor large numbers?
from ipycanvas import Canvas, hold_canvas
import numpy as np
def rgb_to_hex(rgb):
if len(rgb) == 3:
return '#%02x%02x%02x' % rgb
elif len(rgb) == 4:
return '#%02x%02x%02x%02x' % rgb
def Bezier4(t, pts):
p = t**np.arange(0, 4,1)
M=np.matrix([[0,0,0,1],[0,0,3,-3],[0,3,-6,3],[1,-3,3,-1]])
return np.asarray((p*M*pts))
canvas = Canvas(width=800, height=800)
display(canvas) # display the canvas in the output cell..
pts = np.random.randint(50, 750, size=[4, 2]) #choose random starting point
print(pts) #print so we can compare with ending state
d = np.random.uniform(-4,4,size=[4,2]) #some random velocity vectors
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3))) #some random color
canvas.font = '16px serif' #font for displaying the changing pts array
with hold_canvas(canvas):
for ani in range(300):
#logic to bounce the points about...
for n in range(0,len(pts)):
pts[n]=pts[n] + d[n]
if pts[n][0] >= 800 or pts[n][0] <= 0 :
d[n][0] = - d[n][0]
if pts[n][1] >= 800 or pts[n][1] <= 0 :
d[n][1] = - d[n][1]
#calculate the points needed to display a bezier curve
B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,15)]
#begin display output....
canvas.clear()
#first draw bezier curve...
canvas.stroke_style = c
canvas.stroke_lines(B)
#Now draw control points
canvas.stroke_style = rgb_to_hex((255,255,128, 50))
canvas.stroke_lines(pts)
#print the control points to the canvas so we can see them move
canvas.stroke_style = rgb_to_hex((255,255,128, 150))
canvas.stroke_text(str(pts), 10, 32)
canvas.sleep(20)
In all seriousness, I have tried to think through what can be happening and I am coming up blank. Since ipycanvas is talking to the browser/javascript maybe all of the data for the frames are rendered first and the array used to hold the pts data for the stroke_lines ends up with the final values... Whereas the B array is recreated in each loop... It's a guess.
There are two ways to get the code to behave as expected and avoid the unsightly time-traveling code. The first way is to switch the location of the line with hold_canvas(canvas): to inside the loop. This however renders the canvas.sleep(20) line rather useless.
canvas = Canvas(width=800, height=800)
display(canvas)
pts = np.random.randint(50, 750, size=[4, 2])
print(pts)
d = np.random.uniform(-8,8,size=[4,2])
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3)))
canvas.font = '16px serif'
#with hold_canvas(canvas):
for ani in range(300):
with hold_canvas(canvas):
for n in range(0,len(pts)):
if pts[n][0] > 800 or pts[n][0] < 0 :
d[n][0] = -d[n][0]
if pts[n][1] > 800 or pts[n][1] < 50 :
d[n][1] = -d[n][1]
pts[n]=pts[n] + d[n]
B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,25)]
canvas.clear()
canvas.stroke_style = c
canvas.stroke_lines(B)
canvas.stroke_style = rgb_to_hex((255,255,128, 50))
#pts2 = np.copy(pts)
canvas.stroke_lines(pts)
canvas.fill_style = rgb_to_hex((255,255,255, 150))
canvas.fill_circles(pts.T[0], pts.T[1],np.array([4]*4))
canvas.stroke_style = rgb_to_hex((255,255,128, 150))
canvas.fill_text(str(pts), 10, 32)
sleep(20/1000)
#canvas.sleep(20)
In this version, the control lines are updated as expected. This version is a little more "real time" and thus the sleep(20/1000) is needed to
The other way to do it would be just to ensure that a copy of pts is made and passed to canvas.stroke_lines:
canvas = Canvas(width=800, height=800)
display(canvas)
pts = np.random.randint(50, 750, size=[4, 2])
print(pts)
d = np.random.uniform(-8,8,size=[4,2])
c = rgb_to_hex(tuple(np.random.randint(75, 255,size=3)))
canvas.font = '16px serif'
with hold_canvas(canvas):
for ani in range(300):
#with hold_canvas(canvas):
for n in range(0,len(pts)):
if pts[n][0] > 800 or pts[n][0] < 0:
d[n][0] = -d[n][0]
if pts[n][1] > 800 or pts[n][1] < 50:
d[n][1] = -d[n][1]
pts[n]=pts[n] + d[n]
B = [(Bezier4(i, pts)).ravel() for i in np.linspace(0,1,35)]
canvas.clear()
canvas.stroke_style = c
canvas.stroke_lines(B)
canvas.stroke_style = rgb_to_hex((255,255,128, 50))
pts2 = np.copy(pts)
canvas.stroke_lines(pts2)
canvas.fill_style = rgb_to_hex((255,255,255, 150))
canvas.fill_circles(pts.T[0], pts.T[1],np.array([4]*4))
canvas.stroke_style = rgb_to_hex((255,255,128, 150))
canvas.fill_text(str(pts), 10, 32)
#sleep(20/1000)
canvas.sleep(20)
I could not actually find the data passed between the python and the browser but it seems pretty logical that what is happening is that python is finishing its work (and ani loop) before sending the widget instructions on what to draw, and the pts values sent are the final ones.
(yes I know there is a bug in the bouncing logic)
I have code that shows the label for each point in a matplotlib scatterplot using mplcursors, similar to this example. I want to know how to, form a list of values, make a certain point stand out, as in if I have a graph of points y=-x^2. When I go near the peak, it shouldn't show 0.001, but 0 instead, without the trouble needing to find the exact mouse placement of the top. I can't solve for each point in the graph, as I don't have a specific function.
Supposing the points in the scatter plot are ordered, we can investigate whether an extreme in a nearby window is also an extreme in a somewhat larger window. If, so we can report that extreme with its x and y coordinates.
The code below only shows the annotation when we're close to a local maximum or minimum. It also temporarily shows a horizontal and vertical line to indicate the exact spot. The code can be a starting point for many variations.
import matplotlib.pyplot as plt
import mplcursors
import numpy as np
near_window = 10 # the width of the nearby window
far_window = 20 # the width of the far window
def show_annotation(sel):
ind = sel.target.index
near_start_index = max(0, ind - near_window)
y_near = y[near_start_index: min(N, ind + near_window)]
y_far = y[max(0, ind - far_window): min(N, ind + far_window)]
near_max = y_near.max()
far_max = y_far.max()
annotation_str = ''
if near_max == far_max:
near_argmax = y_near.argmax()
annotation_str = f'local max:\nx:{x[near_start_index + near_argmax]:.3f}\ny:{near_max:.3f}'
maxline = plt.axhline(near_max, color='crimson', ls=':')
maxline_x = plt.axvline(x[near_start_index+near_argmax], color='grey', ls=':')
sel.extras.append(maxline)
sel.extras.append(maxline_x)
else:
near_min = y_near.min()
far_min = y_far.min()
if near_min == far_min:
near_argmin = y_near.argmin()
annotation_str = f'local min:\nx:{x[near_start_index+near_argmin]:.3f}\ny:{near_min:.3f}'
minline = plt.axhline(near_min, color='limegreen', ls=':')
minline_x = plt.axvline(x[near_start_index + near_argmin], color='grey', ls=':')
sel.extras.append(minline)
sel.extras.append(minline_x)
if len(annotation_str) > 0:
sel.annotation.set_text(annotation_str)
else:
sel.annotation.set_visible(False) # hide the annotation
# sel.annotation.set_text(f'x:{sel.target[0]:.3f}\n y:{sel.target[1]:.3f}')
N = 500
x = np.linspace(0, 100, 500)
y = np.cumsum(np.random.normal(0, 0.1, N))
box = np.ones(20) / 20
y = np.convolve(y, box, mode='same')
scat = plt.scatter(x, y, s=1)
cursor = mplcursors.cursor(scat, hover=True)
cursor.connect('add', show_annotation)
plt.show()
I am working on a image processing project where i have to perform center surround difference calculation with Earth Mover's distance(EMD) on multiscale level but the problem is that i can't figure it out how center surround difference works and how could i use EMD for it.
I found the python function for EMD but it works with 2 source image histograms whereas in my problem i have only one source.
I am generating multi scales of the image using skimage's pyramid_gaussian function using solution provided on
link: https://gist.github.com/duhaime/211365edaddf7ff89c0a36d9f3f7956c
I tried:
def get_img(path, norm_size=True, norm_exposure=False):
img = imread(path, flatten=True).astype(int)
if norm_size:
img = resize(img, (height, width), anti_aliasing=True, preserve_range=True)
if norm_exposure:
img = normalize_exposure(img)
return img
def get_histogram(img):
h, w = img.shape
hist = [0.0] * 256
for i in range(h):
for j in range(w):
hist[img[i, j]] += 1
return np.array(hist) / (h * w)
def normalize_exposure(img):
img = img.astype(int)
hist = get_histogram(img)
cdf = np.array([sum(hist[:i+1]) for i in range(len(hist))]) # get the sum of vals accumulated by each position in hist
sk = np.uint8(255 * cdf) # determine the normalization values for each unit of the cdf
height, width = img.shape # normalize each position in the output image
normalized = np.zeros_like(img)
for i in range(0, height):
for j in range(0, width):
normalized[i, j] = sk[img[i, j]]
return normalized.astype(int)
def earth_movers_distance(path_a, path_b):
img_a = get_img(path_a, norm_exposure=True)
img_b = get_img(path_b, norm_exposure=True)
hist_a = get_histogram(img_a)
hist_b = get_histogram(img_b)
return wasserstein_distance(hist_a, hist_b)
if __name__ == '__main__':
image = cv2.imread("images/test3.jpg")
pyramidlist=[]
dst = []
for (i, resized) in enumerate(pyramid_gaussian(image, downscale=1.4)):
if resized.shape[0] < 30 or resized.shape[1] < 30:
break
cv2.imshow(f"Layer {i+1}", resized)
cv2.waitKey(0)
pyramidlist.append(resized[i])
print(pyramidlist)
print(len(pyramidlist))
cv2.destroyAllWindows()
but don't know how to use EMD after generating pyramids and calculate center surround difference.
I have a question revolving around what would be a viable approach to placing out random-sized squares on a symmetrical, non-visible grid on a tkinter-canvas. I'm going to explain it quite thoroughly as it's a somewhat proprietary problem.
This far I've tried to solve it mostly mathematically. But I've found it to be quite a complex problem, and it seems reasonable that there would be a better approach to take it on than what I've tried.
In its most basic form the code looks like this:
while x_len > canvas_width:
xpos = x_len + margin
squares[i].place(x=xpos, y=ypos)
x_len += square_size + space
i += 1
x_len is the total width of all the squares on a given row, and resets when exiting the while-loop (eg. when x_len > window width), among with xpos (the position on X), as well as altering Y-axis to create a new row.
When placing same-size squares it looks like this:
So far so good.
However when the squares are of random-size it looks like this (at best):
The core problem, beyond that the layout can be quite unpredictable, is that the squares aren't centered to the "invisible grid" - because there is none.
So to solve this I've tried an approach where I use a fixed distance and a relative distance based on every given square. This yields satisficing results for the Y-axis on the first row, but not on the X-axis, nor the following rows on Y.
See example (where first row is centered on Y, but following rows and X is not):
So with this method I'm using a per-square alteration in both Y- and X-axis, based on variables that I fetch from a list that contain widths for all of the generated squares.
In it's entirety it looks like this (though it's work in progress so it's not very well optimized):
square_widths = [60, 75, 75, 45...]
space = square_size*0.5
margin = (square_size+space)/2
xmax = frame_width - margin - square_size
xmin = -1 + margin
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
m_ypos = 0
extra_x = 0
while len(squares) <= 100:
n=-1
# row_ypos alters y for every new row
row_ypos += 200-square_widths[n]/2
# this if-statement is not relevant to the question
if x < 0:
n=0
xpos = x
extra_x = x
x_len = z
while x_len < xmax:
ypos = row_ypos
extra_x += 100
ypos = row_ypos + (200-square_widths[n])/2
xpos = extra_x + (200-square_widths[n])/2
squares[i].place(x=xpos, y=ypos)
x_len = extra_x + 200
i += 1
n += 1
What's most relevant here is row_ypos, that alters Y for each row, as well as ypos, that alters Y for each square (I don't have a working calculation for X yet). What I would want to achieve is a similar result that I get for Y-axis on the first row; on all rows and columns (eg. both in X and Y). To create a symmetrical grid with squares of different sizes.
So my questions are:
Is this really best practice to solve this?
If so - Do you have any tips on decent calculations that would do the trick?
If not - How would you approach this?
A sidenote is that it has to be done "manually" and I can not use built-in functions of tkinter to solve it.
Why don't you just use the grid geometry manager?
COLUMNS = 5
ROWS = 5
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
This will line everything up, but the randomness may make the rows and columns different sizes. You can adjust that with the row- and columnconfigure functions:
import tkinter as tk
from random import randint
COLUMNS = 10
ROWS = 5
class GUI(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
labels = []
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
labels.append(l)
self.update() # draw everything
max_width = max(w.winfo_width() for w in labels)
max_height = max(w.winfo_height() for w in labels)
for column in range(self.grid_size()[0]):
self.columnconfigure(col, minsize=max_width) # set all columns to the max width
for row in range(self.grid_size()[1]):
self.rowconfigure(row, minsize=max_height) # set all rows to the max height
def main():
root = tk.Tk()
win = GUI(root)
win.pack()
root.mainloop()
if __name__ == "__main__":
main()
I found the culprit that made the results not turn out the way expected, and it wasn't due to the calculations. Rather it turned out that the list I created didn't put the squares in correct order (which I should know since before).
And so I fetched the width from the raw data itself, which makes a lot more sense than creating a list.
The function now looks something like this (again, it's still under refinement, but I just wanted to post this, so that people don't waste their time in coming up with solutions to an already solved problem :)):
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
while len(squares) <= 100:
n=0
if y > 1:
ypos -= max1 + 10
if y < 0:
if ypos < 0:
ypos=10
else:
ypos += max1 + 10 #+ (max1-min1)/2
if x < 0:
n=0
xc=0
xpos = x
x_len = z
while x_len < xmax:
yc = ypos + (max1-squares[i].winfo_width())/2
if xpos <= 0:
xpos = 10
else:
xpos += max1 + 10
xc = xpos + (max1-squares[i].winfo_width())/2
squares[i].place(x=xc, y=yc)
x_len += max1 + 10
print (x_len)
i += 1
n += 1