Confronta direttamente i turni subpixel tra due spettri e ottieni errori credibili


9

Ho due spettri dello stesso oggetto astronomico. La domanda essenziale è questa: come posso calcolare lo spostamento relativo tra questi spettri e ottenere un errore preciso su tale spostamento?

Qualche dettaglio in più se sei ancora con me. Ogni spettro sarà un array con un valore x (lunghezza d'onda), un valore y (flusso) ed errore. Lo spostamento della lunghezza d'onda sarà sub-pixel. Supponiamo che i pixel siano spaziati regolarmente e che venga applicato un solo spostamento della lunghezza d'onda all'intero spettro. Quindi la risposta finale sarà simile a: 0,35 +/- 0,25 pixel.

I due spettri saranno molti continuum senza caratteristiche, punteggiati da alcune caratteristiche di assorbimento (avvallamenti) piuttosto complicate che non si modellano facilmente (e non sono periodiche). Vorrei trovare un metodo che confronta direttamente i due spettri.

Il primo istinto di tutti è quello di fare una correlazione incrociata, ma con i cambiamenti dei subpixel, dovrai interpolare tra gli spettri (livellando prima?) - inoltre, gli errori sembrano cattivi da correggere.

Il mio approccio attuale è di lisciare i dati contorcendomi con un kernel gaussiano, quindi di spline il risultato livellato e confrontare i due spettri scanalati - ma non mi fido di esso (specialmente gli errori).

Qualcuno sa un modo per farlo correttamente?

Ecco un breve programma Python che produrrà due spettri giocattolo che sono spostati di 0,4 pixel (scritti in toy1.ascii e toy2.ascii) con cui puoi giocare. Anche se questo modello di giocattolo utilizza una semplice funzione gaussiana, supponi che i dati effettivi non possano essere adattati a un modello semplice.

import numpy as np
import random as ra
import scipy.signal as ss
arraysize = 1000
fluxlevel = 100.0
noise = 2.0
signal_std = 15.0
signal_depth = 40.0
gaussian = lambda x: np.exp(-(mu-x)**2/ (2 * signal_std))
mu = 500.1
np.savetxt('toy1.ascii', zip(np.arange(arraysize), np.array([ra.normalvariate(fluxlevel, noise) for x in range(arraysize)] - gaussian(np.arange(arraysize)) * signal_depth), np.ones(arraysize) * noise))
mu = 500.5
np.savetxt('toy2.ascii', zip(np.arange(arraysize), np.array([ra.normalvariate(fluxlevel, noise) for x in range(arraysize)] - gaussian(np.arange(arraysize)) * signal_depth), np.ones(arraysize) * noise))

Se capisco correttamente il problema sembra simile alla registrazione delle immagini, tranne per il fatto che hai solo uno spostamento lineare dei sub-pixel su un asse. Forse provare tecniche di registrazione di immagini standard come la correlazione di fase?
Paolo R,

Se si ha un puro ritardo in un segnale (ovvero lo spostamento nel parametro della lunghezza d'onda di cui si parla), si potrebbe essere in grado di sfruttare la proprietà di trasformata di Fourier che trasforma il ritardo in un offset di fase lineare nel dominio della frequenza. Ciò potrebbe funzionare se i due campioni non sono danneggiati da rumori o interferenze di misurazione diversi.
Jason R,

1

1
Hai dati reali con cui testare? Il valore del rumore che hai dato è troppo perché la correlazione incrociata sia accurata sottocampione. Questo è ciò che trova con diversi percorsi di rumore 2.0 e 0.7 Offset (= 1000,7 sull'asse X della trama), per esempio: i.stack.imgur.com/UK5JD.png
endolith

Risposte:


5

Penso che usare la correlazione incrociata e interpolare il picco funzionerebbe bene. Come descritto in Il campionamento prima della correlazione incrociata è inutile? , interpolare o ricampionare prima della correlazione incrociata in realtà non fornisce ulteriori informazioni. Le informazioni sul picco del sottocampione sono contenute nei campioni circostanti. Hai solo bisogno di estrarlo con un errore minimo. Ho raccolto alcuni appunti qui .

Il metodo più semplice è l'interpolazione quadratica / parabolica, di cui ho un esempio Python qui . È presumibilmente esatto se il tuo spettro si basa su una finestra gaussiana o se il picco capita di cadere esattamente sul punto medio tra i campioni, ma per il resto ha qualche errore . Quindi nel tuo caso probabilmente vorrai usare qualcosa di meglio.

Ecco un elenco di stimatori più complicati, ma più accurati. "Dei metodi di cui sopra, il secondo stimatore di Quinn ha il minimo errore RMS."

Non conosco la matematica, ma questo documento afferma che la loro interpolazione parabolica ha una precisione teorica del 5% della larghezza di un bidone FFT.

L'uso dell'interpolazione FFT sull'output di correlazione incrociata non presenta alcun errore di polarizzazione , quindi è il migliore se si desidera una precisione davvero buona. Se è necessario bilanciare la precisione e la velocità di calcolo, si consiglia di eseguire alcune interpolazioni FFT e quindi di seguirla con uno degli altri stimatori per ottenere un risultato "abbastanza buono".

Questo utilizza solo l'adattamento parabolico, ma produce il valore corretto per l'offset se il rumore è basso:

def parabolic_polyfit(f, x, n):
    a, b, c = polyfit(arange(x-n//2, x+n//2+1), f[x-n//2:x+n//2+1], 2)
    xv = -0.5 * b/a
    yv = a * xv**2 + b * xv + c

    return (xv, yv)

arraysize = 1001
fluxlevel = 100.0
noise = 0.3 # 2.0 is too noisy for sub-sample accuracy
signal_std = 15.0
signal_depth = 40.0
gaussian = lambda x: np.exp(-(mu-x)**2/ (2 * signal_std))
mu = 500.1
a_flux = np.array([ra.normalvariate(fluxlevel, noise) for x in range(arraysize)] - gaussian(np.arange(arraysize)) * signal_depth)
mu = 500.8
b_flux = np.array([ra.normalvariate(fluxlevel, noise) for x in range(arraysize)] - gaussian(np.arange(arraysize)) * signal_depth)

a_flux -= np.mean(a_flux)
b_flux -= np.mean(b_flux)

corr = ss.fftconvolve(b_flux, a_flux[::-1])

peak = np.argmax(corr)
px, py = parabolic_polyfit(corr, peak, 13)

px = px - (len(a_flux) - 1)
print px

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Il rumore nel tuo campione produce risultati che variano di più di un intero campione, quindi l'ho ridotto. Adattare la curva usando più punti del picco aiuta a restringere un po 'la stima, ma non sono sicuro che sia statisticamente valido, e in realtà peggiora la stima per la situazione di rumore più basso.

Con rumore = 0,2 e adattamento a 3 punti, fornisce valori come 0,398 e 0,402 per offset = 0,4.

Con rumore = 2.0 e adattamento a 13 punti, fornisce valori come 0,156 e 0,595 per offset = 0,4.


Sto cercando di risolvere questo esatto problema per la registrazione delle immagini. Ho bisogno della precisione del sub-pixel (0,1 sarebbe probabilmente abbastanza buono) ma soprattutto non ho bisogno di distorsioni, quindi i metodi di interpolazione non funzionano. Esistono metodi validi (e implementati in Python?) Per questo? Il metodo zero padding funzionerà, ma è troppo costoso per essere pratico.
keflavich,

@kelavich: hai testato tutti i metodi di interpolazione e hai riscontrato errori inaccettabili? L'approccio raccomandato è una combinazione di alcuni zero-padding seguiti da un'interpolazione a basso errore. Non conosco nessun altro metodo, ma scommetto che questo ti fornirebbe molta precisione.
endolith,

Sì, ho trovato un pregiudizio inaccettabile nell'interpolazione lineare e del 2 ° ordine. Ho provato il padding zero FFT, ma il risultato è dominato dal ringing ad alta frequenza ... c'è qualche possibilità che tu possa aggiungere un esempio di padding zero?
keflavich,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.