Draw an ellipse on a figure and get coordinates_Python - python

I'm working on Python 2.7. I have to define some Areas of Interest (AoI) on a picture. Basically, I'm trying to do this drawing an ellipse (or more) on a specific part of the picture and to get the coordinates (x; y) of its contour. I want to save these coordinates on a file, in order to use them later to see whether (or not) my data are inside this area.
This is my code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Ellipse, Circle
from matplotlib.path import Path
# Get an example image
img = imread('sposa.png')
# Create a figure. Equal aspect so circles look circular
fig,ax = plt.subplots(1)
ax.set_aspect('equal')
# Show the image
ax.imshow(img)
ax.set_xlim(0,1600)
ax.set_ylim(0,1200)
# Now, loop through coord arrays, and create a circle at each x,y pair
ellipse = Ellipse((1000, 400), width=400, height=100, edgecolor='white',facecolor='none',linewidth=2)
ax.add_patch(ellipse)
path = ellipse.get_path()
# Show the image
plt.show()
When I run the code, I get this (that is exactly what I want):
However, when I print the path in order to check it, I get the following output, which (I suppose) is exclusively related to the ellipse.
Path(array([[ 0. , -1. ],
[ 0.2652031 , -1. ],
[ 0.51957987, -0.89463369],
[ 0.70710678, -0.70710678],
[ 0.89463369, -0.51957987],
[ 1. , -0.2652031 ],
[ 1. , 0. ],
[ 1. , 0.2652031 ],
[ 0.89463369, 0.51957987],
[ 0.70710678, 0.70710678],
[ 0.51957987, 0.89463369],
[ 0.2652031 , 1. ],
[ 0. , 1. ],
[-0.2652031 , 1. ],
[-0.51957987, 0.89463369],
[-0.70710678, 0.70710678],
[-0.89463369, 0.51957987],
[-1. , 0.2652031 ],
[-1. , 0. ],
[-1. , -0.2652031 ],
[-0.89463369, -0.51957987],
[-0.70710678, -0.70710678],
[-0.51957987, -0.89463369],
[-0.2652031 , -1. ],
[ 0. , -1. ],
[ 0. , -1. ]]), array([ 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 79], dtype=uint8))
However, I need a list of coordinates of the ellipse in relation to the pixel of the picture (1600 X 1200). I'm probably using the wrong function or there is something that does not match between the picture and the ellipse.
I should obtain something like this (this is an example from a previous experiment):
[ Path(array([[ 1599. , 868.86791294],
[ 1598. , 868.87197971],
[ 1597. , 868.8801087 ],
...,
[ 1597. , 675.30378536],
[ 1598. , 675.31373204],
[ 1599. , 675.31870792]]), None)]
665
Can anyone help me?
Thank you in advance,
R

You should use Ellipse.get_path().vertices, however it's not in the correct coordinates system. To transform it, apply ellipse.get_patch_transform().transform to it. See below a working example
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
from matplotlib.path import Path
from matplotlib.patches import PathPatch
img = plt.imread("image.jpg")
fig, ax = plt.subplots(1)
ax.set_aspect('equal')
ax.imshow(img)
# Create the base ellipse
ellipse = Ellipse((300, 300), width=400, height=100,
edgecolor='white', facecolor='none', linewidth=2)
# Get the path
path = ellipse.get_path()
# Get the list of path vertices
vertices = path.vertices.copy()
# Transform the vertices so that they have the correct coordinates
vertices = ellipse.get_patch_transform().transform(vertices)
# You can then save the vertices array to a file: csv, pickle... It's up to you
plt.show()

the path array appears to be a coarse normalized circle - I'd ignore it
you have the ellipse info already
ellipse = Ellipse((1000, 400), width=400, height=100, ...)
I would just do a sin, cos paramaterized ellipse based on the 1st few numbers in Ellipse((1000, 400), width=400, height=100which are the center point and axis lengths if you want to draw a hi res ellipse explicitly
for a membership test (x - x_0)^2/a^2 + (y - y_0)^2/b^2 <= 1 is probably best where a, b
are 1/2 of the respective width=400, height=100
of course you would only need to test pixel indices within the bounding rectangle

Related

Plotting contour in python

I have an np array of shape (15,2)
When I meshgrid first and second dimension of this array and write a function of this array, and then plot using contour, the output is blank. I don't understand what's wrong. Please guide me.
The code is:
M=[[12.647,4.2439],[13.744,8.9295],[13.93,9.191 ],[16.223,12.452 ],
[16.475 , 12.784 ],
[16.535 , 12.813 ],
[16.774 , 13.377 ],
[16.949 , 13.465 ],
[17.295 , 13.494 ],
[17.329 , 13.613 ],
[18.183 , 14.683 ],
[19.055 , 14.871 ],
[19.098 , 15.487 ],
[21.798 , 16.23 ],
[22.241 , 16.411 ]]
x1,y1=np.meshgrid(M[:,0],M[:,1])
F=np.sqrt(x1**2 + y1**2)
plt.contour(x1,y1,F)
Here M is a np array of (15,2). Is the problem with the values of M?
This is what i get when i run
import numpy as np
import matplotlib.pyplot as plt
M = np.array([[12.647, 4.2439],
[13.744, 8.9295],
[13.93, 9.191],
[16.223, 12.452],
[16.475, 12.784],
[16.535, 12.813],
[16.774, 13.377],
[16.949, 13.465],
[17.295, 13.494],
[17.329, 13.613],
[18.183, 14.683],
[19.055, 14.871],
[19.098, 15.487],
[21.798, 16.23],
[22.241, 16.411]])
x1, y1 = np.meshgrid(M[:, 0], M[:, 1])
f = np.sqrt(x1 ** 2 + y1 ** 2)
plt.contour(x1, y1, f)
plt.show()
Is this what you want? If not, please explain in detail what you would expect to see.

Generating a spline using scipy from a numpy array containing x and y coordinates [duplicate]

I have a set of points pts which form a loop and it looks like this:
This is somewhat similar to 31243002, but instead of putting points in between pairs of points, I would like to fit a smooth curve through the points (coordinates are given at the end of the question), so I tried something similar to scipy documentation on Interpolation:
values = pts
tck = interpolate.splrep(values[:,0], values[:,1], s=1)
xnew = np.arange(2,7,0.01)
ynew = interpolate.splev(xnew, tck, der=0)
but I get this error:
ValueError: Error on input data
Is there any way to find such a fit?
Coordinates of the points:
pts = array([[ 6.55525 , 3.05472 ],
[ 6.17284 , 2.802609],
[ 5.53946 , 2.649209],
[ 4.93053 , 2.444444],
[ 4.32544 , 2.318749],
[ 3.90982 , 2.2875 ],
[ 3.51294 , 2.221875],
[ 3.09107 , 2.29375 ],
[ 2.64013 , 2.4375 ],
[ 2.275444, 2.653124],
[ 2.137945, 3.26562 ],
[ 2.15982 , 3.84375 ],
[ 2.20982 , 4.31562 ],
[ 2.334704, 4.87873 ],
[ 2.314264, 5.5047 ],
[ 2.311709, 5.9135 ],
[ 2.29638 , 6.42961 ],
[ 2.619374, 6.75021 ],
[ 3.32448 , 6.66353 ],
[ 3.31582 , 5.68866 ],
[ 3.35159 , 5.17255 ],
[ 3.48482 , 4.73125 ],
[ 3.70669 , 4.51875 ],
[ 4.23639 , 4.58968 ],
[ 4.39592 , 4.94615 ],
[ 4.33527 , 5.33862 ],
[ 3.95968 , 5.61967 ],
[ 3.56366 , 5.73976 ],
[ 3.78818 , 6.55292 ],
[ 4.27712 , 6.8283 ],
[ 4.89532 , 6.78615 ],
[ 5.35334 , 6.72433 ],
[ 5.71583 , 6.54449 ],
[ 6.13452 , 6.46019 ],
[ 6.54478 , 6.26068 ],
[ 6.7873 , 5.74615 ],
[ 6.64086 , 5.25269 ],
[ 6.45649 , 4.86206 ],
[ 6.41586 , 4.46519 ],
[ 5.44711 , 4.26519 ],
[ 5.04087 , 4.10581 ],
[ 4.70013 , 3.67405 ],
[ 4.83482 , 3.4375 ],
[ 5.34086 , 3.43394 ],
[ 5.76392 , 3.55156 ],
[ 6.37056 , 3.8778 ],
[ 6.53116 , 3.47228 ]])
Actually, you were not far from the solution in your question.
Using scipy.interpolate.splprep for parametric B-spline interpolation would be the simplest approach. It also natively supports closed curves, if you provide the per=1 parameter,
import numpy as np
from scipy.interpolate import splprep, splev
import matplotlib.pyplot as plt
# define pts from the question
tck, u = splprep(pts.T, u=None, s=0.0, per=1)
u_new = np.linspace(u.min(), u.max(), 1000)
x_new, y_new = splev(u_new, tck, der=0)
plt.plot(pts[:,0], pts[:,1], 'ro')
plt.plot(x_new, y_new, 'b--')
plt.show()
Fundamentally, this approach not very different from the one in #Joe Kington's answer. Although, it will probably be a bit more robust, because the equivalent of the i vector is chosen, by default, based on the distances between points and not simply their index (see splprep documentation for the u parameter).
Your problem is because you're trying to work with x and y directly. The interpolation function you're calling assumes that the x-values are in sorted order and that each x value will have a unique y-value.
Instead, you'll need to make a parameterized coordinate system (e.g. the index of your vertices) and interpolate x and y separately using it.
To start with, consider the following:
import numpy as np
from scipy.interpolate import interp1d # Different interface to the same function
import matplotlib.pyplot as plt
#pts = np.array([...]) # Your points
x, y = pts.T
i = np.arange(len(pts))
# 5x the original number of points
interp_i = np.linspace(0, i.max(), 5 * i.max())
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
plt.show()
I didn't close the polygon. If you'd like, you can add the first point to the end of the array (e.g. pts = np.vstack([pts, pts[0]])
If you do that, you'll notice that there's a discontinuity where the polygon closes.
This is because our parameterization doesn't take into account the closing of the polgyon. A quick fix is to pad the array with the "reflected" points:
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
#pts = np.array([...]) # Your points
pad = 3
pts = np.pad(pts, [(pad,pad), (0,0)], mode='wrap')
x, y = pts.T
i = np.arange(0, len(pts))
interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad))
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
plt.show()
Alternately, you can use a specialized curve-smoothing algorithm such as PEAK or a corner-cutting algorithm.
Using the ROOT Framework and the pyroot interface I was able to generate the following image
With the following code(I converted your data to a CSV called data.csv so reading it into ROOT would be easier and gave the columns titles of xp,yp)
from ROOT import TTree, TGraph, TCanvas, TH2F
c1 = TCanvas( 'c1', 'Drawing Example', 200, 10, 700, 500 )
t=TTree('TP','Data Points')
t.ReadFile('./data.csv')
t.SetMarkerStyle(8)
t.Draw("yp:xp","","ACP")
c1.Print('pydraw.png')
To fit a smooth closed curve through N points you can use line segments with the following constraints:
Each line segment has to touch its two end points (2 conditions per line segment)
For each point the left and right line segment have to have the same derivative (2 conditions per point == 2 conditions per line segment)
To be able to have enough freedom for in total 4 conditions per line segment the equation of each line segment should be y = ax^3 + bx^2 + cx + d. (so the derivative is y' = 3ax^2 + 2bx + c)
Setting the conditions as suggested would give you N * 4 linear equations for N * 4 unknowns (a1..aN, b1..bN, c1..cN, d1..dN) solvable by matrix inversion (numpy).
If the points are on the same vertical line special (but simple) handling is required since the derivative will be "infinite".

Fitting a closed curve to a set of points

I have a set of points pts which form a loop and it looks like this:
This is somewhat similar to 31243002, but instead of putting points in between pairs of points, I would like to fit a smooth curve through the points (coordinates are given at the end of the question), so I tried something similar to scipy documentation on Interpolation:
values = pts
tck = interpolate.splrep(values[:,0], values[:,1], s=1)
xnew = np.arange(2,7,0.01)
ynew = interpolate.splev(xnew, tck, der=0)
but I get this error:
ValueError: Error on input data
Is there any way to find such a fit?
Coordinates of the points:
pts = array([[ 6.55525 , 3.05472 ],
[ 6.17284 , 2.802609],
[ 5.53946 , 2.649209],
[ 4.93053 , 2.444444],
[ 4.32544 , 2.318749],
[ 3.90982 , 2.2875 ],
[ 3.51294 , 2.221875],
[ 3.09107 , 2.29375 ],
[ 2.64013 , 2.4375 ],
[ 2.275444, 2.653124],
[ 2.137945, 3.26562 ],
[ 2.15982 , 3.84375 ],
[ 2.20982 , 4.31562 ],
[ 2.334704, 4.87873 ],
[ 2.314264, 5.5047 ],
[ 2.311709, 5.9135 ],
[ 2.29638 , 6.42961 ],
[ 2.619374, 6.75021 ],
[ 3.32448 , 6.66353 ],
[ 3.31582 , 5.68866 ],
[ 3.35159 , 5.17255 ],
[ 3.48482 , 4.73125 ],
[ 3.70669 , 4.51875 ],
[ 4.23639 , 4.58968 ],
[ 4.39592 , 4.94615 ],
[ 4.33527 , 5.33862 ],
[ 3.95968 , 5.61967 ],
[ 3.56366 , 5.73976 ],
[ 3.78818 , 6.55292 ],
[ 4.27712 , 6.8283 ],
[ 4.89532 , 6.78615 ],
[ 5.35334 , 6.72433 ],
[ 5.71583 , 6.54449 ],
[ 6.13452 , 6.46019 ],
[ 6.54478 , 6.26068 ],
[ 6.7873 , 5.74615 ],
[ 6.64086 , 5.25269 ],
[ 6.45649 , 4.86206 ],
[ 6.41586 , 4.46519 ],
[ 5.44711 , 4.26519 ],
[ 5.04087 , 4.10581 ],
[ 4.70013 , 3.67405 ],
[ 4.83482 , 3.4375 ],
[ 5.34086 , 3.43394 ],
[ 5.76392 , 3.55156 ],
[ 6.37056 , 3.8778 ],
[ 6.53116 , 3.47228 ]])
Actually, you were not far from the solution in your question.
Using scipy.interpolate.splprep for parametric B-spline interpolation would be the simplest approach. It also natively supports closed curves, if you provide the per=1 parameter,
import numpy as np
from scipy.interpolate import splprep, splev
import matplotlib.pyplot as plt
# define pts from the question
tck, u = splprep(pts.T, u=None, s=0.0, per=1)
u_new = np.linspace(u.min(), u.max(), 1000)
x_new, y_new = splev(u_new, tck, der=0)
plt.plot(pts[:,0], pts[:,1], 'ro')
plt.plot(x_new, y_new, 'b--')
plt.show()
Fundamentally, this approach not very different from the one in #Joe Kington's answer. Although, it will probably be a bit more robust, because the equivalent of the i vector is chosen, by default, based on the distances between points and not simply their index (see splprep documentation for the u parameter).
Your problem is because you're trying to work with x and y directly. The interpolation function you're calling assumes that the x-values are in sorted order and that each x value will have a unique y-value.
Instead, you'll need to make a parameterized coordinate system (e.g. the index of your vertices) and interpolate x and y separately using it.
To start with, consider the following:
import numpy as np
from scipy.interpolate import interp1d # Different interface to the same function
import matplotlib.pyplot as plt
#pts = np.array([...]) # Your points
x, y = pts.T
i = np.arange(len(pts))
# 5x the original number of points
interp_i = np.linspace(0, i.max(), 5 * i.max())
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
plt.show()
I didn't close the polygon. If you'd like, you can add the first point to the end of the array (e.g. pts = np.vstack([pts, pts[0]])
If you do that, you'll notice that there's a discontinuity where the polygon closes.
This is because our parameterization doesn't take into account the closing of the polgyon. A quick fix is to pad the array with the "reflected" points:
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
#pts = np.array([...]) # Your points
pad = 3
pts = np.pad(pts, [(pad,pad), (0,0)], mode='wrap')
x, y = pts.T
i = np.arange(0, len(pts))
interp_i = np.linspace(pad, i.max() - pad + 1, 5 * (i.size - 2*pad))
xi = interp1d(i, x, kind='cubic')(interp_i)
yi = interp1d(i, y, kind='cubic')(interp_i)
fig, ax = plt.subplots()
ax.plot(xi, yi)
ax.plot(x, y, 'ko')
plt.show()
Alternately, you can use a specialized curve-smoothing algorithm such as PEAK or a corner-cutting algorithm.
Using the ROOT Framework and the pyroot interface I was able to generate the following image
With the following code(I converted your data to a CSV called data.csv so reading it into ROOT would be easier and gave the columns titles of xp,yp)
from ROOT import TTree, TGraph, TCanvas, TH2F
c1 = TCanvas( 'c1', 'Drawing Example', 200, 10, 700, 500 )
t=TTree('TP','Data Points')
t.ReadFile('./data.csv')
t.SetMarkerStyle(8)
t.Draw("yp:xp","","ACP")
c1.Print('pydraw.png')
To fit a smooth closed curve through N points you can use line segments with the following constraints:
Each line segment has to touch its two end points (2 conditions per line segment)
For each point the left and right line segment have to have the same derivative (2 conditions per point == 2 conditions per line segment)
To be able to have enough freedom for in total 4 conditions per line segment the equation of each line segment should be y = ax^3 + bx^2 + cx + d. (so the derivative is y' = 3ax^2 + 2bx + c)
Setting the conditions as suggested would give you N * 4 linear equations for N * 4 unknowns (a1..aN, b1..bN, c1..cN, d1..dN) solvable by matrix inversion (numpy).
If the points are on the same vertical line special (but simple) handling is required since the derivative will be "infinite".

Using homogeneous transforms (non-affine) with matplotlib patches

I have an application where I am using matplotlib to display elliptical regions on an image. To do this I'm using mpl.patches.Circle along with mp.patches.Affine2D to warp unit circles into an elliptical shape.
import numpy as np
import matplotlib as mpl
import pyplot as plt
invVR_mats = np.array([
[[ 7.80247545, 0. , 92.9254837 ],
[ -3.46026921, 10.85727882, 17.53866959],
[ 0. , 0. , 1. ]],
[[ 11.42656994, 0. , 76.86006927],
[ -3.26515651, 9.61946297, 24.79440498],
[ 0. , 0. , 1. ]],
[[ 10.40444851, 0. , 140.62428284],
[ -10.94557095, 10.59212685, 24.91024971],
[ 0. , 0. , 1. ]],])
invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
for invVR in invVR_mats]
ell_actors = [mpl.patches.Circle((0, 0), 1, transform=invVR)
for invVR in invVR_aff2Ds]
coll = mpl.collections.PatchCollection(ell_actors)
plt.figure()
ax = plt.gca()
ax.set_ylim(0, 100)
ax.set_xlim(0, 300)
ax.add_collection(coll)
There is a point in my application that the ellipses in one image are put in correspondence with ellipses from a second image using a homography matrix. So far I have been using it to warp points from image1 into image2.
I would like to get a visual idea of how these ellipses warp into image2. I can transform my affine matrices with this homography matrix, but the resulting matrix is no longer affine. (I believe it represents a general conic, either a circle, ellipse, hyperbola, or parabola)
from numpy.core.umath_tests import matrix_multiply
H = np.array([[ -0.70098, 0.12273, 5.18734],
[ 0.12444, -0.63474, 14.13995],
[ 0.00004, 0.00025, -0.64873]])
HinvVR_mats = matrix_multiply(H, invVR_mats)
print(HinvVR_mats)
#---------
np.array([
[[ -5.89405808e+00, 1.33251383e+00, -5.77990446e+01],
[ 3.16731132e+00, -6.89154916e+00, 1.45711021e+01],
[ -5.52968284e-04, 2.71431970e-03, -6.40628313e-01]],
[[ -8.41052966e+00, 1.18059669e+00, -4.56470140e+01],
[ 3.49444781e+00, -6.10585793e+00, 7.96641640e+00],
[ -3.59226330e-04, 2.40486574e-03, -6.39456996e-01]],
[[ -8.63666024e+00, 1.29997173e+00, -9.03302348e+01],
[ 8.24232128e+00, -6.72324660e+00, 1.58277039e+01],
[ -2.32021480e-03, 2.64803171e-03, -6.36877466e-01]]])
If I de-homogenize only the last column I can find the center of where the ellipse was projected, but I would like to see some shape information as well.
So far the best I've done is just de-homogenizing the last column and ignoring the values in [:, 2, 0] and [:, 2, 1]
HinvVR_mats = np.divide(HinvVR_mats , HinvVR_mats[:, None, None, 2, 2])
print(HinvVR_mats)
array([[[ 9.20043332e+00, -2.08001083e+00, 9.02224323e+01],
[ -4.94407015e+00, 1.07574845e+01, -2.27450173e+01],
[ 8.63165541e-04, -4.23696494e-03, 1.00000000e+00]],
[[ 1.31526118e+01, -1.84624877e+00, 7.13840248e+01],
[ -5.46471120e+00, 9.54850438e+00, -1.24580956e+01],
[ 5.61767769e-04, -3.76079354e-03, 1.00000000e+00]],
[[ 1.35609449e+01, -2.04116458e+00, 1.41832989e+02],
[ -1.29417694e+01, 1.05565779e+01, -2.48520394e+01],
[ 3.64311021e-03, -4.15783546e-03, 1.00000000e+00]]])
Is there a way I can tranform mpl.patches.Circle (or any other patch for that matter) using a non-affine matrix. The documentation seems to suggest it is possible, but I'm not seeing any way to go about it.
I have
I was able to solve this by looking at the tutorial posted by tcaswell
I had to create my own tranformation class though which looked like this
class HomographyTransform(mpl.transforms.Transform):
"""
References:
http://stackoverflow.com/questions/28401788/using-homogeneous-transforms-non-affine-with-matplotlib-patches?noredirect=1#comment45156353_28401788
http://matplotlib.org/users/transforms_tutorial.html
"""
input_dims = 2
output_dims = 2
is_separable = False
def __init__(self, H, axis=None, use_rmin=True):
mpl.transforms.Transform.__init__(self)
self._axis = axis
self._use_rmin = use_rmin
self.H = H
def transform_non_affine(self, input_xy):
"""
The input and output are Nx2 numpy arrays.
"""
import vtool as vt
_xys = input_xy.T
xyz = vt.add_homogenous_coordinate(_xys)
xyz_t = vt.matrix_multiply(self.H, xyz)
xy_t = vt.remove_homogenous_coordinate(xyz_t)
output_xy = xy_t.T
return output_xy
#transform_non_affine.__doc__ = mpl.transforms.Transform.transform_non_affine.__doc__
def transform_path_non_affine(self, path):
vertices = path.vertices
if len(vertices) == 2 and vertices[0, 0] == vertices[1, 0]:
return mpl.path.Path(self.transform(vertices), path.codes)
ipath = path.interpolated(path._interpolation_steps)
return mpl.path.Path(self.transform(ipath.vertices), ipath.codes)
#transform_path_non_affine.__doc__ = mpl.transforms.Transform.transform_path_non_affine.__doc__
The functions called by my own library vtool are:
def add_homogenous_coordinate(_xys):
assert _xys.shape[0] == 2
_zs = np.ones((1, _xys.shape[1]), dtype=_xys.dtype)
_xyzs = np.vstack((_xys, _zs))
return _xyzs
def remove_homogenous_coordinate(_xyzs):
assert _xyzs.shape[0] == 3
_xys = np.divide(_xyzs[0:2], _xyzs[None, 2])
return _xys
and matrix_multiply is the same matrix_multiply used earlier.
and my function to create the transform matrices currently looks like this:
def get_invVR_aff2Ds(kpts, H=None):
""" Returns matplotlib keypoint transformations (circle -> ellipse) """
#invVR_mats = ktool.get_invV_mats(kpts, with_trans=True, with_ori=True)
invVR_mats = ktool.get_invVR_mats3x3(kpts)
if H is None:
invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
for invVR in invVR_mats]
else:
# not actually affine
invVR_aff2Ds = [HomographyTransform(H.dot(invVR))
for invVR in invVR_mats]
return invVR_aff2Ds

Python Data Structure Recommendations for a 2D Grid

I am looking to implement an Ant Colony Optimization algorithm in Python, though am new to both Python and Object Oriented Programming so the learning curve has been rather steep. At this point, I am stuck as to how to address the following situation:
As ants walk around a 2D grid, they will encounter obstacles, pheromone deposits by other ants, food, etc. What data structure do I use to represent this 2D world and the aforementioned properties of each cell?
I had tried a 2D array, thinking that array[x-coord][y-coord] could point to a {} (dictionary) with the appropriate properties (Obstacle: 'Yes / 'No', Pheromone Level: X %, etc.). Unfortunately, though NumPy lets me create a 2D array, I cannot assign dictionary objects to the various coordinates.
from numpy import *
myArray = array([[1,2,3,4],
[5,6,7,8],
[9,10,11,12]])
myArray[2][2]={}
Returns:
Traceback (most recent call last):
File "/Users/amormachine/Desktop/PythonTest.py", line 7, in <module>
myArray[2][2]={}
TypeError: long() argument must be a string or a number, not 'dict'
[Finished in 0.6s with exit code 1]
I am not committed to either dictionaries or this paradigm for implementing this project and would certainly appreciate the wisdom of the group.
sure you can, you just cant if your dtype is int ... so make your array with objects and you can use objects...
In [43]: a = [[{},{},{}],[{},{},{}]]
In [44]: a = numpy.array(a)
In [45]: a[1][1] = {'hello':'world','something':5}
In [46]: a
Out[46]:
array([[{}, {}, {}],
[{}, {'hello': 'world', 'something': 5}, {}]], dtype=object)
although not sure whay you will gain using numpy with objects, you may be better off just leaving it as a list of lists
In plain Python I would be going for the list-of-dicts approach but with NumPy I find it more natural to work with separate arrays for different attributes rather than trying to keep things in one structure.
import numpy as np
grid_shape = (120,80)
# example of random initialization with this grid shape
pheremone_level = np.random.rand(*grid_shape)
obstacle = np.random.rand(*grid_shape) > 0.8
As #bitwise says it entirely depends on what operations you want to perform. Generally the "correct" way in NumPy will be much closer to how you would write it in Matlab than non-NumPy Python. Unfortunately I'm not familiar with how Ant Colony Optimization works so I can't say what's more suitable.
I was looking for something related to structured 2D grids and google led me to this page.
Although my solution is not entirely related to grids for what has been asked in the question, and I didn't want to repeat a question for 'structured 2D grid' data structure, I'm posting my solution here. I hope it will be useful to the audience searching for 2D structured grid and redirected here by search engines
Note: the method returns only the cell vertices and the vertex connectivity of each cells. Other quantities like cell volume, cell centroid, circumcircle, incircle, etc as needed for the application can be easily generated by adding additional routines
import numpy as np
import matplotlib.pyplot as plt
def create_structured_grid(corner1=None, corner2=None, nx=5, ny=5, plt_=True, annotate=True):
"""
creates a structured grid of rectangular lattice
input:
------
corner1 : [x_start, y_start]
corner2 : [x_end, y_end]
nx : numpts in x
ny : numpts in y
plt_ : boolean whether to plot or not
annotate: whether to annotate the grid points or not
output:
-------
vertex_array : numpy.array((numpts, dim),dtype=float) of vertices
connectivity : numpy.array((num_cells, 2**dim), dtyp=int) of
vertex connectivity for each cell
plots : additionally plots if boolean values are true
"""
#corner1 = np.array([0.0, 0.0])
#corner2 = np.array([1.0, 1.0])
dim = len(corner1) #currently only for 2D,
x_pts = np.linspace(corner1[0], corner2[0], nx)
y_pts = np.linspace(corner1[1], corner2[1], ny)
Xv, Yv = np.meshgrid(x_pts, y_pts)
numpts = nx*ny
vertex_array = np.zeros((numpts, 2), dtype=float)
vertex_array[:,0] = np.reshape(Xv, numpts)
vertex_array[:,1] = np.reshape(Yv, numpts)
num_cells = int(nx-1)*(ny-1)
connectivity = np.zeros((num_cells, int(2**dim)), dtype=int)
rows = ny-1
cols = nx-1
for row in range(rows):
for col in range(cols):
num = nx*row + col
connectivity[cols*row + col] = [num+0, num+1, num+nx, num+nx+1]
if plt_:
X,Y = vertex_array.T
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_aspect('equal')
plt.scatter(X,Y, marker='o', s=50, color='g', alpha=1.0)
plt.plot(Xv,Yv, linewidth=2, color='k')
plt.plot(Yv,Xv, linewidth=2, color='k')
if annotate:
for idx, cc in enumerate(vertex_array):
plt.text(cc[0], cc[1], str(idx), color='k', verticalalignment='bottom', horizontalalignment='right', fontsize='medium')
plt.show(block=False)
return vertex_array, connectivity
A call to function can be like this:
c1 = np.array([0.0, 0.0])
c2 = np.array([1.0, 1.0])
vertices, connctivity = create_structured_grid(corner1=c1, corner2=c2, nx=4, ny=4)
vertices = array([[ 0. , 0. ],
[ 0.33333333, 0. ],
[ 0.66666667, 0. ],
[ 1. , 0. ],
[ 0. , 0.33333333],
[ 0.33333333, 0.33333333],
[ 0.66666667, 0.33333333],
[ 1. , 0.33333333],
[ 0. , 0.66666667],
[ 0.33333333, 0.66666667],
[ 0.66666667, 0.66666667],
[ 1. , 0.66666667],
[ 0. , 1. ],
[ 0.33333333, 1. ],
[ 0.66666667, 1. ],
[ 1. , 1. ]])
connectivity = array([[ 0, 1, 5, 6],
[ 1, 2, 6, 7],
[ 2, 3, 7, 8],
[ 4, 5, 9, 10],
[ 5, 6, 10, 11],
[ 6, 7, 11, 12],
[ 8, 9, 13, 14],
[ 9, 10, 14, 15],
[10, 11, 15, 16]])

Categories