I am trying to estimate/interpolate a curve from noisy data like the circle in the example. My data consists of more than circles but this should be a good starting point for solving the other structures as well.
I have a noisy binary image and I am trying to fit a continuous curve/skeleton to it (each pixel has 2 neighbours, except maybe start and end pixel, if the shape is not circular).
I had some success fitting the x,y coordinates separately, using the distance to a starting point as x values and the coordinates as y value and then interpolating distances in small steps. Then I checked if the coordinates were all connected. In some extreme cases the new interpolated points are not connected and I have to use smaller steps for the interpolation. This often also leads to pixels with more than 2 neighbours and other weird artifacts.
Is there an easier way to fit these values to a curve and to get a continuous curve as a result?
import numpy as np
from skimage import draw
from matplotlib import pyplot as plt
image = np.zeros((200,200), dtype=np.uint8)
coords = np.array(draw.circle_perimeter(100,100,50))
noise = np.random.normal(0,2,coords.shape).astype(np.int64)
coords += noise
image[coords[0], coords[1]] = 1
plt.imshow(image, cmap="gray")
plt.show()
To fit data, you need a model. There are any number of ways of fitting a circle. The one I've had the most success with is Ian Coope's linearized solution. The paper is available here: https://ir.canterbury.ac.nz/handle/10092/11104
I've made a python implementation of it in a linearized fitting library called scikit-guess. The function is skg.nsphere_fit. Given your (2, n) array coords, you would use it like this:
from skg import nsphere_fit
radius, center = nsphere_fit(coords, axis=0)
To plot over your image, you can use matplotlib.patches.Circle:
from matplotlib.patches import Circle
fig, ax = plt.subplots()
ax.imshow(image, cmap='gray')
ax.add_patch(Circle(center[::-1], radius, edgecolor='red', facecolor='none'))
You need to reverse center because your input coordinates are (row, col), while Circle expects (x, y), which is (col, row).
To fit a different model, you would need a different method. For arbitrary models, you might want to look into scipy.optimize and lmfit.
Fitting a circle to noisy data is very simple :
This method comes from https://fr.scribd.com/doc/14819165/Regressions-coniques-quadriques-circulaire-spherique
I need to compute (and plot) a histogram2d but my binning grid is rotated and also non-orthogonal.
A way of doing this could be to apply a transformation to my data so I get it into a cartesian system, compute my histogram2d and then apply the inverse transformation.
Can this be done directly without this overhead transformation ?
I guess my question is: how do I define the bins for my histogram2d in this case ? (AFAIK, histogram2d will only accept x and y aligned bins)
My data is 2 huge lists of points (10k~100k each), the coordinates of which are given in a cartesian coordinate system (actually a projected CRS because these are real-world locations) but they are organized in a regular grid that is not aligned to X and Y axis (rotated) and that may or may not be orthogonal. The binning grid will be derived from it so it will be a (rotated) regular quadrilaterals grid.
I have seen that matplotlib has a QuadMesh object (see here) so I'm being hopeful but I'm not sure how to handle this in NumPy.
Basically this is what I want to achieve:
After some testing, I came to the conclusion that the overhead of transforming the coordinates into a Cartesian grid to compute the histogram and back for plotting is acceptable. Matrix operations in NumPy are fairly efficient and I can handle 115+ million points in less than 7 sec.
However, the "back" part can be handled by Matplotlib directly with matplotlib.transforms.
pcolormesh, hist2d and imshow all accept a transform keyword which can be used to plot the Cartesian data into the desired coordinates like so:
# set I, J, bins (in the Cartesian system) and cmap
# a, b, c, d, e, f are values of the transformation matrix
transform = matplotlib.transforms.Affine2D.from_values(a, b, c, f, d, e, f)
fig, ax = plt.subplots(figsize=figsize)
_, _, _, im = ax.hist2d(I, J, bins=bins, cmap=cmap, transform=transform + ax.transData)
fig.colorbar(im)
ax.autoscale()
It is not really much faster than handling the "back" conversion with NumPy but it can make the code lighter as it only requires 1 additional line and 1 additional keyword.
imshow can be a little bit of a pain as it won't update the display extent after using ax.autoscale() and it handles coordinates as images or matrix so the transform has to be adjusted accordingly. For these reasons, I prefer hist2d.
References:
https://matplotlib.org/3.1.1/api/transformations.html#module-matplotlib.transforms
https://matplotlib.org/3.1.1/tutorials/advanced/transforms_tutorial.html
I have an input data with 6 columns where the three first are the position x, y, z and the rest are vector components of a vector field. I could only make a 3d graph with quiver3d of the mayavi library in python (x,y,z,px,py,pz) creating 6 numpy arrays x,y,z,px,py,pz just to visualize it.
It would be great to get a 3D graph by any mean where I could insert cut planes where the vectors that are contained on it would be shown, or planes where I could see a color map that would help me to understand its beahaviour. Any help?
Here is the input:
https://pastebin.com/raw/pmGguxUc
Here is the code I used to get the visualization with quiver3d function.
import numpy as np
import mayavi.mlab as mlab
data = np.loadtxt("vectorfield.dat", dtype = float)
dataTranspuesta=data.T
x=dataTranspuesta[0]
y=dataTranspuesta[1]
z=dataTranspuesta[2]
px=dataTranspuesta[3]
py=dataTranspuesta[4]
pz=dataTranspuesta[5]
mlab.quiver3d(x, y, z, px, py, pz, line_width=0.7 ,scale_mode="vector" ,scale_factor=0.0045,mask_points=7 ,mode="arrow", colormap="seismic" )
mlab.show()
It is easier to organize everything using mayavi's pipeline. They are basically the same as using mlab plotting functions, but organize your visualization tasks in a pipeline fashion.
Pfld = mlab.pipeline.vector_field(x, y, z, px, py, pz)
Quiver = mlab.pipeline.vectors(Pfld)
Pcut = mlab.pipeline.vector_cut_plane(Quiver, plane_orientation='x_axes')
You can also draw isosurface contours for the vectors' magnitude
Pmag = mlab.pipeline.extract_vector_norm(Pfld)
Piso = mlab.pipeline.iso_surface(Pmag)
and plane cuts of the scalar field can be achieved through mlab.pipeline.scalar_cut_plane(Pmag) or mlab.pipeline.image_plane_widget(Pmag)
See documentations for more details on the allowed arguments, decorations, etc.
Also examples 1 and
exmaples 2 may fit your needs.
I have x,y,z data that define a surface (x and y position, z height).
The data is imperfect, in that it contains some noise, i.e. not every point lies precisely on the plane I wish to model, just very close to it.
I only have data within a triangular region, not the full x,y, plane.
Here is an example with z represented by colour:
In this example the data has been sampled in the centres of triangles on a mesh like this (each blue dot is a sample):
If it is necessary, the samples could be evenly spaced on an x,y grid, though a solution where this is not required is preferable.
I want to represent this data as a sum of sines and cosines in order to manipulate it mathematically. Ideally using as few terms as are needed to keep the error of the fit acceptably low.
If this were a square region I would take the 2D Fourier transform and discard higher frequency terms.
However I think this situation has two key differences that make this approach not viable:
Ideally I want to use samples at the points indicated by the blue dots in my grid above. I could instead use a regular x,y grid if there is no alternative, but this is not an ideal solution
I do not have data for the whole x,y, plane. The white areas in the first image above do not contain data that should be considered in the fit.
So in summary my question is thus:
Is there a way to extract coefficients for a best-fit of this data using a linear combination of sines and cosines?
Ideally using python.
My apologies if this is more of a mathematics question and stack overflow is not the correct place to post this!
EDIT: Here is an example dataset in python style [x,y,z] form - sorry it's huge but apparently I can't use pastebin?:
[[1.7500000000000001e-08, 1.0103629710818452e-08, 14939.866751020554],
[1.7500000000000001e-08, 2.0207259421636904e-08, 3563.2218207404617],
[8.7500000000000006e-09, 5.0518148554092277e-09, 24529.964593228644],
[2.625e-08, 5.0518148554092261e-09, 24529.961688158553],
[1.7500000000000001e-08, 5.0518148554092261e-09, 21956.74682671843],
[2.1874999999999999e-08, 1.2629537138523066e-08, 10818.190869824304],
[1.3125000000000003e-08, 1.2629537138523066e-08, 10818.186813746233],
[1.7500000000000001e-08, 2.5259074277046132e-08, 3008.9480862705223],
[1.3125e-08, 1.7681351993932294e-08, 5630.9978116591838],
[2.1874999999999999e-08, 1.768135199393229e-08, 5630.9969846863969],
[8.7500000000000006e-09, 1.0103629710818454e-08, 13498.380006002562],
[4.3750000000000003e-09, 2.5259074277046151e-09, 40376.866196753763],
[1.3125e-08, 2.5259074277046143e-09, 26503.432370909999],
[2.625e-08, 1.0103629710818452e-08, 13498.379635232159],
[2.1874999999999999e-08, 2.5259074277046139e-09, 26503.430698738041],
[3.0625000000000005e-08, 2.525907427704613e-09, 40376.867011915041],
[8.7500000000000006e-09, 1.2629537138523066e-08, 11900.832515759088],
[6.5625e-09, 8.8406759969661469e-09, 17422.002946526718],
[1.09375e-08, 8.8406759969661469e-09, 17275.788904632376],
[4.3750000000000003e-09, 5.0518148554092285e-09, 30222.756636780832],
[2.1875000000000001e-09, 1.2629537138523088e-09, 64247.241146490327],
[6.5625e-09, 1.2629537138523084e-09, 35176.652106572205],
[1.3125e-08, 5.0518148554092277e-09, 22623.574247287044],
[1.09375e-08, 1.2629537138523082e-09, 27617.700396641056],
[1.5312500000000002e-08, 1.2629537138523078e-09, 25316.907231576402],
[2.625e-08, 1.2629537138523066e-08, 11900.834523905782],
[2.4062500000000001e-08, 8.8406759969661469e-09, 17275.796410700641],
[2.8437500000000002e-08, 8.8406759969661452e-09, 17422.004617294893],
[2.1874999999999999e-08, 5.0518148554092269e-09, 22623.570035270699],
[1.96875e-08, 1.2629537138523076e-09, 25316.9042194055],
[2.4062500000000001e-08, 1.2629537138523071e-09, 27617.700160860692],
[3.0625000000000005e-08, 5.0518148554092261e-09, 30222.765972585737],
[2.8437500000000002e-08, 1.2629537138523069e-09, 35176.65151453446],
[3.2812500000000003e-08, 1.2629537138523065e-09, 64247.246775422129],
[2.1875000000000001e-09, 2.5259074277046151e-09, 46711.23463223876],
[1.0937500000000001e-09, 6.3147685692615553e-10, 101789.89315354674],
[3.28125e-09, 6.3147685692615543e-10, 52869.788364220134],
[3.2812500000000003e-08, 2.525907427704613e-09, 46711.229428833962],
[3.1718750000000001e-08, 6.3147685692615347e-10, 52869.79233902022],
[3.3906250000000006e-08, 6.3147685692615326e-10, 101789.92509671643],
[1.0937500000000001e-09, 1.2629537138523088e-09, 82527.848790063814],
[5.4687500000000004e-10, 3.1573842846307901e-10, 137060.87432327325],
[1.640625e-09, 3.157384284630789e-10, 71884.380087542726],
[3.3906250000000006e-08, 1.2629537138523065e-09, 82527.861035177877],
[3.3359375000000005e-08, 3.1573842846307673e-10, 71884.398689011548],
[3.4453125000000001e-08, 3.1573842846307663e-10, 137060.96214950032],
[4.3750000000000003e-09, 6.3147685692615347e-09, 18611.868317256733],
[3.28125e-09, 4.4203379984830751e-09, 27005.961455364879],
[5.4687499999999998e-09, 4.4203379984830751e-09, 28655.126635802204],
[3.0625000000000005e-08, 6.314768569261533e-09, 18611.869287539808],
[2.9531250000000002e-08, 4.4203379984830734e-09, 28655.119850641502],
[3.1718750000000001e-08, 4.4203379984830726e-09, 27005.959731047784]]
Nothing stops you from doing normal linear least squares with whatever basis you like. (You'll have to work out the periodicity you want, as mikuszefski said.) The lack of samples outside the triangle will naturally blind the method to the function's behavior out there. You probably want to weight the samples according to the area of their mesh cell, to avoid overfitting the corners.
Here some code that might help to fit periodic spikes. That also shows the use of the base x, x/2+ sqrt(3)/2 * y. The flat part can then be handled by low order Fourier. I hope that gives an idea. (BTW I agree with Davis Herring that area weighting is a good idea). For the fit, I guess, good initial guesses are crucial.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def gauss(x,s):
return np.exp(-x**2/(2.*s**2))
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.15)
Y = np.arange(-5, 5, 0.15)
X, Y = np.meshgrid(X, Y)
kX=np.sin(X)
kY=np.sin(0.5*X+0.5*np.sqrt(3.)*Y)
R = np.sqrt(kX**2 + kY**2)
Z = gauss(R,.4)
#~ surf = ax.plot_wireframe(X, Y, Z, linewidth=1)
surf= ax.plot_surface(X, Y, Z, rstride=1, cstride=1,linewidth=0, antialiased=False)
plt.show()
Output:
I am looking to generate a linear gradient for a polycollection in matplotlib. I have the following code that gives me a single block color
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
v.append(zip(x, y, z))
poly3dCollection = Poly3DCollection(v, facecolor='yellow', edgecolors='none')
I have played around with colormaps and interpolation but I can't quite seem to get this to work in the way I want. Specifically I need a linear gradient based on a number of greyscale values I supply (so no default jet color scheme). The gradient can be set once i.e. top x% of each polygon black then various shades of gray for same x% rather than this needing to be dynamically calculated for each polygon.
I did get the effect I wanted by saving to SVG and manually editing the image, but I need to do this all in code.
It strikes me this should be very straight forward so im not sure what I am missing