Hi ich habe zwei Signale (A und B). Signal B soll in A gefundne werden. Dabei gilt A ist länger (also mehr Messwerte) als B. Dazu verwende ich in C++ die FFT-Library FFTW und berechne die Korrelation. - A und B auf Länge länge(A)+länge(B)-1 bringen ("rechtsseitiges zeropadding") - FFT von A und B bilden - Beide FFTs elementweise multiplizieren, eines aber komplex konjugiert Ergebnis = FFT(A) * conj(FFT(B)) - Inverse FFT des Ergebnis IFFT(Ergebnis) - Maxima in Inverse suchen. Maximaposition ist die gesuchte Position. Das funktioniert soweit wie erwartet. Mein Problem ist aber dass die Länge von A variablen ist (aber stehts größer/gleich der Länge von B) und ich so schnell wie möglich sein will. D.h. ich will, wenn möglich, FFT(B) nur einmalig berechnen. Das Pronlem ist ja dass ich mit länge(A)+länge(B)-1 rechne. Kann ich das irgendwie machen? Gruß Christoph
"Schnelle Faltung" mit Overlap-Save oder Overlap-Add (http://inst.eecs.berkeley.edu/~ee123/fa11/docs/FastConv.pdf). Was sind denn typische Längen von A und B?
Danke. A typisch 300-1200 messwerte B typisch 100-300 messwerte nur so eine größenordnung ;-)
Wie sieht denn der Quellcode dazu aus? Finde ich ziemlich interessant, da ich etwas ähnliches vorhabe (Laufzeitmessung). Vielleicht finde ich noch etwas dazu.
Also das PDF von Florian konnte ich noch nicht bearbeiten, geschweige denn in Code umsetzen. Habe gerade auch andere Probleme an denen ich hänge, deswegen muss die Performance-Geschichte erstmal warten. Meinen bisherigen Code darfst du aber gerne haben ;-) Es ist aber nur ein Testprogramm! Ich verwende die Library FFTW für die Korrelation und die Library OpenCV weil ich später auch mit Bildern arbeite. (Deswegen is der Code auch umfangreicher als nötig) Ich nehme ein Bild X und betrachte es Zeilenweise als Signal X_Zeile. Dann nehme ich den festen Ausschnitt X_Template aus X_Zeile (z.B. Pixel 100 bis 600) und korreliere X_Template mit X_Zeile (also z.B. 800 einzelne Korrelationen bei einem Bild der Höhe 800 px). Für jede Korrelation wird das Maximim gesucht und als Ergebnis gespeichert. Ideal sollte überall 100 herauskommen (mit den Beipielwerten). (Tut es aber nicht) Anschließend bilde suche ich den Median aller Ergebnisse. Er entspricht i.d.R. den gesuchten 100. (Wenn das Template zu ist dann nicht mehr) Verbesserungswürdig ist die Art der Korrelation da nicht mittelwertfrei oder normiert. (Zum testen reicht erstmal die "normale" Kreuzkorrelation) Was ich später nicht haben will ist dass ich jedes mal alle Daten (fft-daten etc) allokiere, die Datansätze fülle etc. Das kostet Zeit.
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <iostream> |
4 | #include <opencv2/opencv.hpp> |
5 | #include <fftw3.h> |
6 | #include <fstream> |
7 | #include "fftw_samples.h" |
8 | #include <time.h> |
9 | |
10 | void fft_1d(); |
11 | int compare(const void * a, const void * b); |
12 | |
13 | using namespace std; |
14 | |
15 | int main (int argc, char * const argv[]) { |
16 | |
17 | fft_1d(); |
18 | |
19 | return 0; |
20 | } |
21 | |
22 | void fft_1d(){ |
23 | |
24 | clock_t c_start, c_init, c_fft, c_log, c_tmp; |
25 | |
26 | int matchloc_should_be = 100+1; //+1 wegen matlab |
27 | int width_of_template = 600; |
28 | cv::Mat image = cv::imread("abc.bmp",0); |
29 | cv::Mat templ = cv::Mat(image,cv::Rect(matchloc_should_be,0,width_of_template,image.size().height)).clone(); |
30 | |
31 | cv::Mat image_sm; |
32 | cv::Mat templ_sm; |
33 | pyrDown(image, image_sm); |
34 | pyrDown(templ, templ_sm); |
35 | imshow("image", image_sm); |
36 | imshow("templ", templ_sm); |
37 | cvWaitKey(0); |
38 | cv::destroyAllWindows(); |
39 | |
40 | printf("Matchloc should be: %d \n", matchloc_should_be); |
41 | printf("image - width: %d height: %d \n", image.size().width, image.size().height); |
42 | printf("templ - width: %d height: %d \n", templ.size().width, templ.size().height); |
43 | |
44 | |
45 | c_tmp = clock(); |
46 | c_start = clock(); |
47 | |
48 | // length of data (both, image and template data must have same length) |
49 | int size_w = image.size().width + templ.size().width - 1; |
50 | |
51 | // number of lines must be the same |
52 | int size_h = image.size().height; |
53 | |
54 | //fftw plans |
55 | fftw_plan plan_forward, plan_backward; |
56 | |
57 | // Pointer to current data |
58 | // fftw_complex * curr_image_data, *cur_templ_data; |
59 | |
60 | // new array of pointers for image data |
61 | fftw_complex ** image_data = new fftw_complex*[size_h]; |
62 | fftw_complex ** image_fft = new fftw_complex*[size_h]; |
63 | |
64 | // new array of pointers for template data |
65 | fftw_complex ** templ_data = new fftw_complex*[size_h]; |
66 | fftw_complex ** templ_fft = new fftw_complex*[size_h]; |
67 | |
68 | // new array of pointers for result data |
69 | fftw_complex ** corr_data = new fftw_complex*[size_h]; |
70 | fftw_complex ** ifft_data = new fftw_complex*[size_h]; |
71 | |
72 | // allocate data for image and template |
73 | for (int i = 0; i < size_h; i++) { |
74 | image_data[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
75 | image_fft[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
76 | |
77 | templ_data[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
78 | templ_fft[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
79 | |
80 | corr_data[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
81 | ifft_data[i] = ( fftw_complex* ) fftw_malloc( sizeof( fftw_complex ) * size_w ); |
82 | } |
83 | |
84 | //zero padding image, template and result data |
85 | for (int i = 0; i < size_h; i++) { |
86 | for (int e = 0; e < size_w ; e++) { |
87 | image_data[i][e][0] = 0.0; // real part |
88 | image_data[i][e][1] = 0.0; // imaginary part |
89 | |
90 | templ_data[i][e][0] = 0.0; // real part |
91 | templ_data[i][e][1] = 0.0; // imaginary part |
92 | |
93 | corr_data[i][e][0] = 0.0; // real part |
94 | ifft_data[i][e][1] = 0.0; // imaginary part |
95 | } |
96 | } |
97 | |
98 | // populate image data |
99 | for (int i = 0; i < size_h; i++) { |
100 | for (int e = 0; e < image.size().width ; e++) { |
101 | image_data[i][e][0] = (double)(image.at<uchar>(i,e)); // real part |
102 | //image_data[i][e][1] = 0.0; // imaginary part |
103 | } |
104 | } |
105 | |
106 | // populate template data |
107 | for (int i = 0; i < size_h; i++) { |
108 | for (int e = 0; e < templ.size().width ; e++) { |
109 | templ_data[i][e][0] = (double)(templ.at<uchar>(i,e));; // real part |
110 | //templ_data[i][e][1] = 0.0; // imaginary part |
111 | } |
112 | } |
113 | |
114 | c_init = clock() - c_tmp; |
115 | c_tmp = clock(); |
116 | |
117 | // calculate fft of image and template data |
118 | for (int i = 0; i < size_h; i++) { |
119 | plan_forward = fftw_plan_dft_1d(size_w, image_data[i], image_fft[i], FFTW_FORWARD, FFTW_ESTIMATE ); |
120 | fftw_execute( plan_forward ); |
121 | |
122 | plan_forward = fftw_plan_dft_1d(size_w, templ_data[i], templ_fft[i], FFTW_FORWARD, FFTW_ESTIMATE ); |
123 | fftw_execute( plan_forward ); |
124 | } |
125 | |
126 | //calculate correlation result in frequency domain |
127 | double a,b,c,d; |
128 | for (int i = 0; i < size_h; i++) { |
129 | for (int e = 0; e < size_w; e++) { |
130 | a = image_fft[i][e][0]; // real |
131 | b = image_fft[i][e][1]; // imaginary |
132 | c = templ_fft[i][e][0]; // real |
133 | d = templ_fft[i][e][1]; // imaginary |
134 | |
135 | corr_data[i][e][0] = a*c + b*d; |
136 | corr_data[i][e][1] = b*c - a*d; |
137 | } |
138 | } |
139 | |
140 | //inverse transform result |
141 | for (int i = 0; i < size_h; i++) { |
142 | plan_backward = fftw_plan_dft_1d(size_w, corr_data[i], ifft_data[i], FFTW_BACKWARD, FFTW_ESTIMATE ); |
143 | fftw_execute( plan_backward ); |
144 | } |
145 | |
146 | //find maxima in result |
147 | double * max = new double[size_h]; |
148 | int * maxpos = new int[size_h]; |
149 | for (int i = 0; i < size_h; i++) { |
150 | max[i] = 0; |
151 | maxpos[i] = 0; |
152 | } |
153 | for (int i = 0; i < size_h; i++) { |
154 | for (int e = 0; e < size_w; e++) { |
155 | if(ifft_data[i][e][0] > max[i]) { |
156 | max[i] = ifft_data[i][e][0]; |
157 | maxpos[i] = e; |
158 | } |
159 | } |
160 | } |
161 | |
162 | c_fft = clock() - c_tmp; |
163 | c_tmp = clock(); |
164 | |
165 | // save to disk for matlab |
166 | ofstream myfile; |
167 | myfile.open ("log.txt"); |
168 | for (int e = 0; e < size_w; e++){ |
169 | for (int i = 0; i < size_h; i++) { |
170 | myfile << ifft_data[i][e][0] << " "; |
171 | } |
172 | myfile << endl; |
173 | } |
174 | myfile.close(); |
175 | |
176 | myfile.open ("log_corr_cpp.txt"); |
177 | for (int i = 0; i < size_h; i++){ |
178 | myfile << maxpos[i] << " " << max[i] << endl; |
179 | } |
180 | myfile.close(); |
181 | |
182 | c_log = clock() - c_tmp; |
183 | c_tmp = clock(); |
184 | |
185 | // find median of maxima positions |
186 | qsort((void*)maxpos, size_h, sizeof(int), compare); |
187 | int median = 0; |
188 | if(size_w%2){ // uneven |
189 | median = maxpos[size_h/2 + 1]; |
190 | } |
191 | else { |
192 | median = (maxpos[size_h/2] + maxpos[size_h/2+1])/2; |
193 | } |
194 | printf("median found at: %d",median); |
195 | |
196 | // log sorted correlation result |
197 | myfile.open ("log_corr_sorted_cpp.txt"); |
198 | for (int i = 0; i < size_h; i++){ |
199 | myfile << maxpos[i] << " " << max[i] << endl; |
200 | } |
201 | myfile.close(); |
202 | |
203 | printf("\ntime: init %f - fft %f - log %f \n", |
204 | (float)c_init/(float)CLOCKS_PER_SEC, |
205 | (float)c_fft/(float)CLOCKS_PER_SEC, |
206 | (float)c_log/(float)CLOCKS_PER_SEC); |
207 | } |
208 | |
209 | int compare (const void * a, const void * b) |
210 | { |
211 | return ( *(int*)a - *(int*)b ); |
212 | } |
Danke für den Code! Scheint ja mehr als reichlich zu sein. Da wir hier gerade beim Thema sind: Ich möchte ein einfaches Echolot / Sonar auf MCU Basis realisieren. Dazu soll die Korrelation zum Einsatz kommen. Ich bin gerade dabei mich in das Thema einzulesen. Jedenfalls hab ich mittlerweile begriffen, dass man mittels Kreuzkorrelation die Laufzeit zwischen 2 Signalen bestimmen kann. Gedanklich hab ich folgendes vor: - Impuls erzeugen und aussenden (erstes Signal ist bekannt) - Echo empfangen und Signal puffern - jetzt das erste Signal im zweiten suchen - Differenz ist dann die doppelte Laufzeit Das ähnelt ja sehr dem Verfahren von Christoph. Jetzt habe ich gelesen, dass durch die Korrelation auch bildgebende Algorithmen möglich sind, um beispielsweise eine Darstellung wie auf einem richtigen Sonar zu haben. Weiß einer wie das funktioniert? Das kann ich mir gerade nämlich nicht vorstellen.
Florian F. schrieb: > Jetzt habe ich gelesen, dass durch die Korrelation auch bildgebende > Algorithmen möglich sind, um beispielsweise eine Darstellung wie auf > einem richtigen Sonar zu haben. Weiß einer wie das funktioniert? Das > kann ich mir gerade nämlich nicht vorstellen. Das ist jetzt nur mein Verständnis der Sache ohne großartig Literatur zu lesen: "Bildgebend" ist das Verfahren doch immer dann, wenn du eine Reihe von Daten, die du durch dein Verfahren bestimmt hast, interpretierts. Z.B. Ein UBoot bewegt sich über den Meeresgrund, misst die Signallaufzeit zum Grund in festen Intervallen. Über die Laufzeit ist die Entfernung bestimmbar (z.B. Schallgeschwindigkeit unter Wasser). Somit kannst du ein 1-D Höhenprofil des Grundes (in Fahrtrichtung des UBootes) erstellen. Was ich damit aber sagen will: IMHO ist das "bildgebende Verfahren" nur die Verwertung deiner Messdaten und hat erst einmal nichts mit dem Messverfahren zu tun. Das ganze kann man natürlich noch sinnvoll verbessern. Z.B. wäre es bei dem UBoot angebracht den Grund in einer gewissen Breite abzutasten. Banale Lösung wäre: Einfach mehrer Sensoren parallel anordnen, dann hat man maherer 1-D Streifen und kann so ein 2-D Bild erstellen. Aber wieder bleibt das Messprinzip unberührt. Hier wir aber der Nutzen der Korrelation deutlich: Die Sensoren können gleichzeitig Arbeiten, sie müssten aber nur jeder ein hinreichend unterschiedliches Signal aussenden, welches die anderen in iher Signalverarbeitung nicht stört.
Christoph abc schrieb: > Das kostet Zeit. Das kostet das Scrollen durch deinen Code auch, insbesondere auf Mobilgeräten. Ist bei dir die Funktion zum Hochladen von Anhängen kaputt?
Florian F. schrieb: > - Echo empfangen und Signal puffern > > - jetzt das erste Signal im zweiten suchen Wie willst du wissen, dass das, was Du empfängts, ein Echo ist und zu wissen, was du Puffern musst? Wenn Es das Echo ist, musst Du nicht suchen. Wenn Du suchst, um es erst zu finden, dann musst Du alles aufnehmen und mehrfach suchen.
Ich nehme alles auf und lass das Signal samplen. Wenn ich schon aufnehme, bevor ich einen Ping losschicke, habe ich bei der Aufnahme am Anfang ein ziemlich starkes Signal, was gleich als Muster herhalten kann und zur Synchronisation dient. Danach mache ich eine diskrete, normierte Korrelation über das gesamte Signal und suche darin das Muster. Am Anfang werde ich somit ein Signal mit einem Korrelationskoeffizienten von 1 erhalten, was mein Muster ist. Alle Signale danach sind entweder Rauschen oder das Echo. Das Echo wird sicherlich auch irgendwas um die 0,5-0,9 als Korrelationskoeffizienten haben. Wie sollte man es sonst am besten machen?
Florian F. schrieb: > Wie sollte man es sonst am besten machen? Hört sich plausibel an, so. Ist die Frage, wie engmaschig du suchst, also von sample 1 ... sample 1024 und dann sample 2 ... saple 1025 oder gröber. im Frequenzbereich (FFT) müsste es einfacher sein, weil dann die Phase rausfällt, wegen / sin/cos.
ich weiß jetzt nicht genau, was du machen möchtest, aber ein matched filter sollte doch genau das tun, was du suchst, oder? der Vorteil ist, er arbeitet im Zeitbereich. Du musst nur eine Faltung programmieren: du faltest das gemessene Signal mit dem Signal, das du finden möchtest und machst eine Grenzwert- und/oder Spitzendetektierung. http://en.wikipedia.org/wiki/Matched_filter ich habe keine Ahnung von Kreuzkorrelation, aber wenn die Aufgabenstellung tatsächlich eine "Suche" ist, ist mir nicht klar, wofür der Frequenzbereich nötig ist. das Phasenargument kann ich nicht ganz nachvollziehen, denn die Faltung arbeitet ja kontinuierlich. Was also in den Daten ist, wird gefunden. Oder nicht?
Florian, ich habe so etwas einmal ausprobiert; es funktionierte auch ganz gut (siehe letzter Beitrag in diesem Thread: Beitrag "Ultraschall Merkmalsvektoren"). Als Signal wird bei diesem Verfahren eine Impulsfolge gesendet, die pseudo-zufällig ist, eine sogenanne MLS. Gleichzeitig mit dem Senden wird das empfangene Signal digitalisiert und gespeichert; das direkte Übersprechen vom Sender auf den Empfänger hat bei diesem speziellen Verfahren keinen störenden Einfluss, solange es den Empfänger nicht übersteuert. In meinem Versuchsaufbau hatte ich die beiden US-Kapseln ungefähr parallel ausgerichtet; das reichte schon aus, um diese Bedingung zu erfüllen. Die MLS haben eine besondere mathematische Eigenschaft, die sich trickreich ausnutzen lässt, um das gesendete Signal im empfangenen wiederzufinden. Wie und warum das funktioniert, habe ich in dem .pdf-Dokument beschrieben, das an dem zitierten Beitrag angehängt ist. Die Mathematik dahinter ist etwas ungewohnt, lässt sich aber mit einem einfachen ATmega durchführen, der über genügend viel RAM verfügt. Ciao, mare_crisium
Danke für das viele Feedback. Ich werde mir auf jeden Fall das PDF mal zu Gemüte führen. Den Ansatz, den ich damals beschrieben habe, hat auch funktioniert. Ich habe es nicht weiter verkompliziert. Ich muss nochmal in meinen Unterlagen schauen, wo ich die Formel für die Kreuzkorrelation ist. Damit ging es zumindest eine einfache Echolotanwendung zu realisieren. Im Grunde habe ich die Formeln 1:1 als Programm umgesetzt und erstmal so etwas rumgespielt und getestet. Nachdem alles den Erwartungen entsprochen hat, habe ich es dann mit der FFT realisiert und damit waren dann wirkliche Leistungssprünge zu erreichen. :-)
Falls ihr euch für Korrelationsfunktionen interessiert kann ich euch das Werk von Hänsler ans Herz legen "Statistische Signale, Grundlagen und Anwendungen" Ich durfte seine Vorlesung als Pflichtvorlesung hören und war froh meinen Schein geschafft zu haben :-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.