numpy analog of matplotplotlib contourplot? - python

Having a 2d data array data and two co-ordinate arrays x, y I can plot a contour plot with matplotlib at any given level
import numpy as np
import matplotlib.pyplot as plt
x, y = np.linspace(0, 2*np.pi), np.linspace(0, 2*np.pi)
xx, yy = np.meshgrid(x, y)
data = np.sin(xx) * np.sin(yy)
level = 0.5
contour_ = plt.contour(xx, yy, data, levels=[level])
plt.show()
Now, I am not really interested in plotting, but rather in position of the contour. For example, I want to see if the contour lies within the x, y domain or 'leaks' outside.
I can get a path object containing (x, y) points of the contour by calling
contour_path = contour_.collections[0].get_paths()
My question is whether there are standard tools to get the same (or analogous) information using only numpy, without matplotlib module. Since no plotting is involved, that would be reasonable.

If you read the source code of contour you can find Cntr:
from matplotlib._cntr import Cntr
x, y = np.linspace(0, 2*np.pi), np.linspace(0, 2*np.pi)
xx, yy = np.meshgrid(x, y)
data = np.sin(xx) * np.sin(yy)
level = 0.5
cntr = Cntr(xx, yy, data)
res = cntr.trace(level)
res is a list that contains the pathes and the codes.

If you just have the data field, you can find approximately where the boundary lies;
In [1]: import numpy as np
In [2]: x, y = np.linspace(0, 2*np.pi), np.linspace(0, 2*np.pi)
In [3]: xx, yy = np.meshgrid(x, y)
In [4]: data = np.sin(xx) * np.sin(yy)
In [5]: scan = np.logical_and(data>0.45, data<0.55)
In [6]: a, b = scan.shape
In [7]: for x in range(a):
for y in range(b):
if scan[x,y]:
print('({}, {}),'.format(x,y), end='')
...:
(4, 10),(4, 11),(4, 12),(4, 13),(4, 14),(4, 15),(5, 7),(5, 8),(5, 9),
(5, 16),(5, 17),(6, 6),(6, 7),(6, 18),(6, 19),(7, 5),(7, 6),(7, 19),
(8, 5),(8, 20),(9, 5),(9, 20),(10, 4),(10, 20),(11, 4),(11, 20),
(12, 4),(12, 20),(13, 4),(13, 20),(14, 4),(14, 20),(15, 4),(15, 20),
(16, 5),(16, 20),(17, 5),(17, 19),(18, 6),(18, 18),(18, 19),(19, 6),
(19, 7),(19, 17),(19, 18),(20, 8),(20, 9),(20, 10),(20, 11),(20, 12),
(20, 13),(20, 14),(20, 15),(20, 16),(29, 33),(29, 34),(29, 35),
(29, 36),(29, 37),(29, 38),(29, 39),(29, 40),(29, 41),(30, 31),
(30, 32),(30, 42),(30, 43),(31, 30),(31, 31),(31, 43),(32, 30),
(32, 44),(33, 29),(33, 44),(34, 29),(34, 45),(35, 29),(35, 45),
(36, 29),(36, 45),(37, 29),(37, 45),(38, 29),(38, 45),(39, 29),
(39, 45),(40, 29),(40, 44),(41, 29),(41, 44),(42, 30),(42, 43),
(42, 44),(43, 30),(43, 31),(43, 42),(43, 43),(44, 32),(44, 33),
(44, 40),(44, 41),(44, 42),(45, 34),(45, 35),(45, 36),(45, 37),
(45, 38),(45, 39),
Of course of make the scan range too small, you won't find many points.
In [9]: scan2 = np.logical_and(data>0.49, data<0.51)
In [10]: for x in range(a):
for y in range(b):
if scan2[x,y]:
print('({}, {}),'.format(x,y), end='')
....:
(4, 12),(5, 17),(7, 19),(9, 20),(12, 4),(17, 5),(19, 7),(20, 9),
(29, 40),(30, 42),(32, 44),(37, 45),(40, 29),(42, 30),(44, 32),
(45, 37),

Related

Best suitable approach to find nearest neighbour to (x, y, z) from list of triplets

I am trying to obtain a triplet from list of triplets that is closest to my required triplet incase if it was not found.
For example:
# V_s,V_g,V_r
triplets = [(500, 12, 5),
(400, 15, 2.5),
(400, 15, 3),
(450, 12, 3),
... ,
(350, 14, 3)]
The triple that I am looking for is
req_triplet = (450, 15, 2) #(Vreq_s, Vreq_g, Vreq_r)
How can I achieve this in python, a best suitable strategy to achieve is what I am in need for.
As of now I am thinking to filter the list by finding nearest parameter V_s. From the resulting list filter further by finding nearest to V_g and finally by V_r.
You can compute Euclidean distance with numPy or you can use
numpy.linalg.norm.
Try this:
>>> import numpy as np
>>> def dist(x,y):
... return np.sqrt(np.sum((x-y)**2))
>>> triplets = [(500, 12, 5), (400, 15, 2.5), (400, 15, 3),(450, 12, 3)(350, 14, 3)]
>>> req_triplet = (450, 15, 2)
>>> arr_dst = [np.linalg.norm(np.array(tr) - np.array(req_triplet)) for tr in triplets]
>>> arr_dst = [dist(np.array(tr), np.array(req_triplet)) for tr in triplets]
>>> arr_dst
[50.17967716117751, 50.002499937503124, 50.00999900019995, 3.1622776601683795, 100.00999950005]
>>> idx = np.argmin(arr_dst)
>>> idx
3
>>> triplets[idx]
(450, 12, 3)
You have to define a metric ||.||, then the triplet T that is close to a fixed one F is the one that minimize ||T - F||. You can use a classic Euclidean distance:
import numpy as np
def dist(u, v):
return np.sqrt(np.sum((np.array(u)-np.array(v))**2))
The general strategy would be to Loop through the list, for each element calculate the distance and check if it is the minimum, otherwise keep going on.
In python this would look something like this-
from math import abs
triplets = [(500, 12, 5),
(400, 15, 2.5),
(400, 15, 3),
(450, 12, 3),
... ,
(350, 14, 3)]
req_triplet = (450, 15, 2)
def calc_dist(a,b):
return sum((abs(a[i]-b[i]) for i in range(3))
def find_closest_triple(req_triplet,triplets):
min_ind = None
min_dist = -1
for i,triplet in enumerate(triplets):
if e == req_triplet:
return i
dist = calc_dist(req_triplet,triplet)
if dist < min_dist:
min_ind = i
return min_ind

Estimate missing points in a list of points

I'm generating a list of (x,y) coordinates from detecting a ball's flight in a video. The problem I have is for a few frames in the middle of the video the ball can't be detected, for these frames the list appends (-1,-1).
Is there a way to estimate the true (x,y) coordinates of the ball for these points?
Eg tracked points list being:
pointList = [(60, 40), (55, 42), (53, 43), (-1, -1), (-1, -1), (-1, -1), (35, 55), (30, 60)]
Then returning an estimate of what the 3 (-1,-1) missing coordinates would be with context to the sourounding points (preserving the curve).
If it's a ball then theoretically it should have a parabolic path, you could try and fit a curve ignoring the (-1, -1) and then replace the missing values.
Something like...
import numpy as np
pointList = [(60, 40), (55, 42), (53, 43), (-1, -1), (-1, -1), (-1, -1), (35, 55), (30, 60)]
x, y = list(zip(*[(x, y) for (x, y) in pointList if x>0]))
fit = np.polyfit(x, y, 2)
polynome = np.poly1d(fit)
# call your polynome for missing data, e.g.
missing = (55 - i*(55-35)/4 for i in range(3))
print([(m, polynome(m)) for m in missing])
giving ...
[(55.0, 41.971982486554325), (50.0, 44.426515896714186), (45.0, 47.44514924300471)]
You could use scipys spline to interpolate the missing values:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import splprep, splev
pointList = [(60, 40), (55, 42), (53, 43),
(-1, -1), (-1, -1), (-1, -1),
(35, 55), (30, 60)]
# Remove the missing values
pointList = np.array(pointList)
pointList = pointList[pointList[:, 0] != -1, :]
def spline(x, n, k=2):
tck = splprep(x.T, s=0, k=k)[0]
u = np.linspace(0.0, 1.0, n)
return np.column_stack(splev(x=u, tck=tck))
# Interpolate the points with a quadratic spline at 100 points
pointList_interpolated = spline(pointList, n=100, k=2)
plt.plot(*pointList.T, c='r', ls='', marker='o', zorder=10)
plt.plot(*pointList_interpolated.T, c='b')
If camera is not moving - just the ball and you ignore the wind, then trajectory is parabolic. See: https://en.wikipedia.org/wiki/Trajectory#Uniform_gravity,_neither_drag_nor_wind
In this case fit quadratic function to points which you know and you will get missing ones. Set also error of boundary points in the vicinity of unknown area (point 53,43 and 35, 55) to be 0 or close to 0 (no-error, big weight in interpolation) when fitting so your interpolation will go through these points.
There are some libraries for polynomial fit. E.g. numpy.polyfit:
https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.polynomial.polynomial.polyfit.html

How to use tweening in Python, without losing accuracy?

I've been struggling to use tweening to make mouse movements smooth in Python, I am currently trying to automate some repetitive tasks.
I've tried to use tweening to remove some of the roughness that occurs without smoothing applied, however by doing so I am losing a noticeable amount of accuracy, after all my dy and dx values are getting split by a number I end up with remainders. This could possibly be solved by getting the greatest common factor on both my values (since both dx and dy need to be split by the same number) unfortunately this leads to a too small of a GCD.
Since the mouse cannot move the remainder of a pixel on a screen I end up a with noticeable loss of accuracy.
Question: How to apply tweening on mouse movements, without losing accuracy?
import pytweening
import win32api
import win32con
from time import sleep
dy = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
dx = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]
while True:
count = 0
values = [(pytweening.getPointOnLine(0, 0, x, y, 0.20)) for x, y in zip(dx, dy)]
while win32api.GetAsyncKeyState(win32con.VK_RBUTTON) and win32api.GetAsyncKeyState(win32con.VK_LBUTTON):
if count < len(dx):
for _ in range(5):
win32api.mouse_event(1, int(values[count][0]), int(values[count][1]), 0, 0)
sleep(0.134 / 5)
count += 1
The fundamental problem here is that you are using relative movement in integer amounts, which will not add up to the total movement you are looking for. If you only want to move linearly, you also don't need PyTweening at all. How about this solution?
import win32api
import win32con
from time import sleep
Npoints = 5
sleeptime = 0.134 / Npoints
dys = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
dxs = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]
x, y = win32api.GetCursorPos()
for dx, dy in zip(dxs, dys):
ddx = dx/Npoints
ddy = dy/Npoints
for _ in range(Npoints):
x += ddx
y += ddy
win32api.SetCursorPos(int(x), int(y))
sleep(sleeptime)
Note that there will still be some very small round-off error and that the cursor will move in a straight line between the points. If the cursor starts at (0, 0), this is the shape it will make (the red crosses are the points where the cursor will be set to):
If you wanted to move in smooth curves through the points and you're OK with using numpy and scipy, this will handle that:
import numpy as np
import scipy.interpolate as sci
totalpoints = 50 # you can set this to a larger number to get closer spaced points
x, y = win32api.GetCursorPos()
# work out absolute coordinates of new points
xs = np.cumsum([x, *dxs])
ys = np.cumsum([y, *dys])
# fit spline between the points (s=0 makes the spline hit all the points)
tck, u = sci.splprep([xs, ys], s=0)
# Evaluate the spline and move to those points
for x, y in zip(*sci.splev(np.linspace(0, 1, totalpoints), tck)):
win32api.SetCursorPos(int(x), int(y))
sleep(sleeptime)
This results in positions as shown below:
Question: Tweening, without losing accuracy?
Reference:
PyTweening - getLinePoint()
x, y = getLinePoint(startPoint x, startPoint y, endPoint x, endPoint y, intervall)
The getLinePoint() function finds a point on the provided line.
Cast your lists, dx anddy, into a list of tuple(x, y)
dx = [-35, 6, -55, -43, 0, 17, 29, 38, 42, 42, 38]
dy = [50, 46, 42, 38, 33, 29, 24, 20, 15, 10, 10]
points = list(zip(dx, dy))
print(points)
Output:
[(-35, 50), (6, 46), (-55, 42), (-43, 38), (0, 33), (17, 29), (29, 24), (38, 20), (42, 15), (42, 10), (38, 10)]
Process this list of points in a double for loop.
import pytweening
for startPoint in points:
for endPoint in points:
x, y = pytweening.getPointOnLine(startPoint[0], startPoint[1],
endPoint[0], endPoint[1],
0.20)
x, y = int(x), int(y)
print('{}, '.format((x, y)), end='')
# win32api.mouse_event(1, x, y, 0, 0)
# sleep(0.134)
Output: The End Points are allways reached!
First move from (-35, 50) to (6, 46):
(-35, 50), (-26, 49), (-39, 48), (-36, 47), (-28, 46), (-24, 45),(-22, 44),
(-20, 44), (-19, 43), (-19, 42), (-20, 42), (-2, 46), (6, 46)
... (omitted for brevity)
Last move from (42, 10) to (38, 10):
(42, 10), (41, 10), (23, 18), (31, 17), (19, 16), (21, 15), (30, 14),
(33, 13), (36, 12), (38, 12), (38, 11), (38, 10), (38, 10)
Tested with Python: 3.6 - pytweening: 1.0.3

Python Fit Polynomial to 3d Data

Have a set of data points (x,y,z), and trying to fit a generic quadratic to them using scipy optimize.curve_fit.
I have tried a couple different methods, but can't seem to make it work. Any guidance on why I would be getting this error, or a recommended different method?
Error is "ValueError: operands could not be broadcast together with shapes (2,) (12,)"
import scipy.optimize as optimize
XY = [(11, 70), (11, 75), (11, 80), (11, 85), (12, 70), (12, 75), (12, 80), (12, 85), (13, 70), (13, 75), (13, 80), (13, 85)]
Z = [203.84, 208, 218.4, 235.872, 228.30080000000004, 232.96000000000004, 244.60800000000006, 264.1766400000001, 254.8, 260, 273, 294.84000000000003]
guess = (1,1,1,1,1,1)
def fit(X, a, b, c, d, f, g):
return a + (b*X[0])+(c*X[1])+(d*X[0]**2)+(f*X[1]**2)+(g*X[0]*X[1])
params, cov = optimize.curve_fit(fit, XY, Z, guess)
According to the docs you need XY to be size (k,M) where k is the dimension and M is the number of samples. In your case you've defined XY to be size (M,k).
Try the following
import numpy as np
...
params, cov = optimize.curve_fit(fit, np.transpose(XY), Z, guess)

Python set a column in list of list 2D matrix

So given two lists
y_new = ( 165, 152, 145, 174)
pos_2D = ( (2,3), (32,52), (73,11), (43,97) )
I would like to so something like
pos_2D_new = setCol(2, y_new, pos_2D)
where column 2 is the Y coordinate.
pos_2D_new = ( (2,165), (32,152), (73,145), (43,174) )
How to set a 1D into a 2D tuple in Python?
You can use a generator expression with zip:
pos_2D_new = tuple((x, y) for (x, _), y in zip(pos_2D, y_new))
With your sample input, pos_2D_new would become:
((2, 165), (32, 152), (73, 145), (43, 174))
You can do this with:
pos_2D_new = [ (x, y2) for (x, _), y2 in zip(pos_2D, y_new) ]
or if you want a tuple:
pos_2D_new = tuple((x, y2) for (x, __), y2 in zip(pos_2D, y_new))
We thus concurrently iterate over the pos_2D and ynew, and each time we construct a new tuple (x, y2).
The above is of course not very generic, we can make it more generic, and allow to specify what item to replace, like:
def replace_coord(d, old_pos, new_coord):
return tuple(x[:d] + (y,) + x[d+1:] for x, y in zip(old_pos, new_coord))
So for the x-coordinate you can use replace_coord(0, old_pos, new_x_coord) whereas for the y-coordinate it is replace_coord(1, old_pos, new_y_coord). This also works for coordinates in three or more dimensions.
Which would give
def setCol(idx, coords_1d, coords_nd):
# recalling that indexing starts from 0
idx -= 1
return [
c_nd[:idx] + (c_1d,) + c_nd[idx+1:]
for (c_1d, c_nd) in zip(coords_1d, coords_nd)
]
and
>>> setCol(2, y_new, pos_2D)
[(2, 165), (32, 152), (73, 145), (43, 174)]

Categories

Resources