Ajuste de una curva cerrada a un conjunto de puntos


Tengo un conjunto de puntos pts que forman un bucle y se ve así:

introduzca la descripción de la imagen aquí

Esto es algo similar a 31243002, pero en lugar de poner puntos entre pares de puntos, me gustaría ajustar una curva suave a través de los puntos (las coordenadas se dan al final de la pregunta), así que probé algo similar a scipy documentación sobre Interpolación:

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)

Pero obtengo este error:

ValueError: Error en la entrada datos

¿Hay alguna manera de encontrar tal ajuste?

Coordenadas de los puntos:

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 ]])
Author: Community, 2015-07-16

4 answers

En realidad, no estabas lejos de la solución en tu pregunta.

Utilizando scipy.interpolate.splprep para la interpolación paramétrica B-spline sería el enfoque más simple. También admite curvas cerradas de forma nativa, si proporciona el parámetro per=1,

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()

introduzca la descripción de la imagen aquí

Fundamentalmente, este enfoque no es muy diferente del de la respuesta de @Joe Kington. Aunque, probablemente será un poco más robusto, porque el equivalente del vector i se elige, por defecto, basado en las distancias entre puntos y no simplemente su índice (ver splprep documentación para el parámetro u).

 21
Author: rth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-17 10:07:47

Su problema es porque está tratando de trabajar con x e y directamente. La función de interpolación que está llamando asume que los valores de x están en orden ordenado y que cada valor x tendrá un valor único de y.

En su lugar, necesitará hacer un sistema de coordenadas parametrizado (por ejemplo, el índice de sus vértices) e interpolar x e y por separado usándolo.

Para empezar, considere lo siguiente:

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()

introduzca la descripción de la imagen aquí

No cerré el polígono. Si usted quisiera, usted puede agregar el primer punto al final de la matriz (e. g. pts = np.vstack([pts, pts[0]])

Si haces eso, notarás que hay una discontinuidad donde el polígono se cierra.

introduzca la descripción de la imagen aquí

Esto se debe a que nuestra parametrización no tiene en cuenta el cierre del polgyon. Una solución rápida es rellenar la matriz con los puntos "reflejados":

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()

introduzca la descripción de la imagen aquí

Alternativamente, puede usar un algoritmo especializado de suavizado de curvas como PEAK o un corte de esquinas algoritmo.

 18
Author: Joe Kington,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-16 21:37:05

Usando el Marco RAÍZ y la interfaz pyroot pude generar la siguiente imagen Dibujo usando pyroot

Con el siguiente código(Convertí sus datos a un CSV llamado data.csv por lo que leer en la RAÍZ sería más fácil y dio los títulos de las columnas de 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')
 8
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-16 22:24:47

Para ajustar una curva cerrada suave a través de N puntos, puede usar segmentos de línea con las siguientes restricciones:

  • Cada segmento de línea tiene que tocar sus dos puntos finales (2 condiciones por segmento de línea)
  • Para cada punto el segmento de línea izquierda y derecha tienen que tener la misma derivada (2 condiciones por punto == 2 condiciones por segmento de línea)

Para poder tener suficiente libertad para un total de 4 condiciones por segmento de línea, la ecuación de cada segmento de línea debe ser y = ax^3 + bx^2 + cx + d. (así que la derivada es y' = 3ax^2 + 2bx + c)

Establecer las condiciones como se sugiere le daría N * 4 ecuaciones lineales para N * 4 incógnitas (a1..aN, b1..bN, c1..cN, d1..DN) solucionable por inversión de matriz (numpy).

Si los puntos están en la misma línea vertical, se requiere un manejo especial (pero simple) ya que la derivada será "infinita".

 5
Author: Jacques de Hooge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-16 21:10:23