2011-11-03 14 views
12

Obecnie pracuję z danymi astronomicznymi, wśród których mam obrazy z komet. Chciałbym usunąć gradient tła nieba na tych obrazach ze względu na czas przechwytywania (zmierzch). Pierwszy program, który opracowałem w tym celu, wziął użytkownika z wybranych punktów z "ginput" Matplotliba (x, y), wyciągnął dane dla każdej współrzędnej (z), a następnie obsadził dane w nowej tablicy z "griddata" SciPy.Python 3D dopasowanie powierzchni wielomianowej, zależne od zamówienia

Ponieważ zakłada się, że tło zmienia się tylko nieznacznie, chciałbym dopasować trójwymiarowy wielomian niskiego rzędu do tego zestawu punktów (x, y, z). Jednak „griddata” nie zezwala na zlecenia wejściowego:

griddata(points,values, (dimension_x,dimension_y), method='nearest/linear/cubic') 

Wszelkie pomysły na innej funkcji, które mogą być używane lub sposobu opracowania LEAs kwadratów pasuje, że pozwoli mi kontrolować kolejność?

Odpowiedz

30

Griddata używa dopasowania splajnu. Splajn 3-go rzędu to nie to samo co wielomian 3-go stopnia (zamiast tego jest to inny wielomian 3-go rzędu w każdym punkcie).

Jeśli chcesz dopasować wielomian 2D, 3-go rzędu do danych, wykonaj następujące czynności, aby oszacować 16 współczynników przy użyciu wszystkich swoich punktów danych.

import itertools 
import numpy as np 
import matplotlib.pyplot as plt 

def main(): 
    # Generate Data... 
    numdata = 100 
    x = np.random.random(numdata) 
    y = np.random.random(numdata) 
    z = x**2 + y**2 + 3*x**3 + y + np.random.random(numdata) 

    # Fit a 3rd order, 2d polynomial 
    m = polyfit2d(x,y,z) 

    # Evaluate it on a grid... 
    nx, ny = 20, 20 
    xx, yy = np.meshgrid(np.linspace(x.min(), x.max(), nx), 
         np.linspace(y.min(), y.max(), ny)) 
    zz = polyval2d(xx, yy, m) 

    # Plot 
    plt.imshow(zz, extent=(x.min(), y.max(), x.max(), y.min())) 
    plt.scatter(x, y, c=z) 
    plt.show() 

def polyfit2d(x, y, z, order=3): 
    ncols = (order + 1)**2 
    G = np.zeros((x.size, ncols)) 
    ij = itertools.product(range(order+1), range(order+1)) 
    for k, (i,j) in enumerate(ij): 
     G[:,k] = x**i * y**j 
    m, _, _, _ = np.linalg.lstsq(G, z) 
    return m 

def polyval2d(x, y, m): 
    order = int(np.sqrt(len(m))) - 1 
    ij = itertools.product(range(order+1), range(order+1)) 
    z = np.zeros_like(x) 
    for a, (i,j) in zip(m, ij): 
     z += a * x**i * y**j 
    return z 

main() 

enter image description here

+3

Jest to bardzo eleganckie rozwiązanie problemu. Użyłem twojego sugerowanego kodu, aby pasował do eliptycznej paraboloidy z niewielkimi modyfikacjami, które chcę udostępnić. Zainteresowałem się dopasowaniem tylko rozwiązań liniowych w postaci 'z = a * (x-x0) ** 2 + b * (y-y0) ** 2 + c'. Pełny kod moich zmian można zobaczyć [tutaj] (http://www.nublia.com/dev/stackoverflow/stow_polyfit2d.py). – regeirk

+0

Uwaga: najnowsze wersje numpy, patrz odpowiedź @ klaus poniżej. W czasie mojej oryginalnej odpowiedzi 'polyvander2d', etcetera nie istniało, ale są one obecnie drogą do zrobienia. –

+1

czy to jest wielomian trzeciego rzędu? chyba że rozumiem źle, ale czy nie ma terminu 'X ** 3 * Y ** 3' z rzędu 6? – maxymoo

9

Poniższy realizacja polyfit2d korzysta z dostępnych metod NumPy numpy.polynomial.polynomial.polyvander2d i numpy.polynomial.polynomial.polyval2d

#!/usr/bin/env python3 

import unittest 


def polyfit2d(x, y, f, deg): 
    from numpy.polynomial import polynomial 
    import numpy as np 
    x = np.asarray(x) 
    y = np.asarray(y) 
    f = np.asarray(f) 
    deg = np.asarray(deg) 
    vander = polynomial.polyvander2d(x, y, deg) 
    vander = vander.reshape((-1,vander.shape[-1])) 
    f = f.reshape((vander.shape[0],)) 
    c = np.linalg.lstsq(vander, f)[0] 
    return c.reshape(deg+1) 

class MyTest(unittest.TestCase): 

    def setUp(self): 
     return self 

    def test_1(self): 
     self._test_fit(
      [-1,2,3], 
      [ 4,5,6], 
      [[1,2,3],[4,5,6],[7,8,9]], 
      [2,2]) 

    def test_2(self): 
     self._test_fit(
      [-1,2], 
      [ 4,5], 
      [[1,2],[4,5]], 
      [1,1]) 

    def test_3(self): 
     self._test_fit(
      [-1,2,3], 
      [ 4,5], 
      [[1,2],[4,5],[7,8]], 
      [2,1]) 

    def test_4(self): 
     self._test_fit(
      [-1,2,3], 
      [ 4,5], 
      [[1,2],[4,5],[0,0]], 
      [2,1]) 

    def test_5(self): 
     self._test_fit(
      [-1,2,3], 
      [ 4,5], 
      [[1,2],[4,5],[0,0]], 
      [1,1]) 

    def _test_fit(self, x, y, c, deg): 
     from numpy.polynomial import polynomial 
     import numpy as np 
     X = np.array(np.meshgrid(x,y)) 
     f = polynomial.polyval2d(X[0], X[1], c) 
     c1 = polyfit2d(X[0], X[1], f, deg) 
     np.testing.assert_allclose(c1, 
           np.asarray(c)[:deg[0]+1,:deg[1]+1], 
           atol=1e-12) 

unittest.main() 
Powiązane problemy