Hauptseite | Liste aller Namensbereiche | Klassenhierarchie | Alphabetische Liste | Übersicht | Auflistung der Dateien | Elemente eines Namensbereiches | Datenstruktur-Elemente | Datei-Elemente

findleds.h

gehe zur Dokumentation dieser Datei
00001 #ifndef FINDLEDSMODULE_H
00002 #define FINDLEDSMODULE_H
00003 
00004 #include "../opencvext.h"
00005 #include "imagesource.h"
00006 #include <iostream>
00007 #include <list>
00008 #include "../microeva.h"
00009 #include "../microadam.h"
00010 #include "../image.h"
00011 #include "correlation.h"
00012 #include "hsvrange.h"
00013 
00014 using namespace std;
00015 
00016 namespace seemicro
00017 {
00018 
00025 const uchar MAXMARKS = 100;
00026 
00035 struct FindLEDSModule : public ImageSource
00036 {
00040   ImageSource& src;
00041 
00045   Micro& micro;
00046   MicroEva *eva;
00047 
00051   CorrelationModule &corr;
00052 
00053   HSVRange& range;
00054 
00055   myrect ROI;
00056 
00057   int w, h;// Abkürzungen für img->width, img->height
00058 
00063   mypoint markierungen[MAXMARKS];
00064 
00068   int anzahl[MAXMARKS];
00069 
00073   float abstand[MAXMARKS];
00074 
00078   unsigned short int markiert;
00079 
00083   float gamma[MAXMARKS];
00084 
00088   float gamma0[MAXMARKS];
00089 
00093   float beta[MAXMARKS];
00094 
00101   char gemerkt[MAXMARKS], gewaehlt[MAXMARKS], armnummer[MAXMARKS];
00102 
00103   FindLEDSModule(ImageSource& Asrc, Micro& Am, CorrelationModule& Ac,
00104     HSVRange& Ar) :
00105     src(Asrc), micro(Am), corr(Ac), range(Ar)
00106   {
00107     eva = dynamic_cast<MicroEva*>(&micro);
00108     if(src.img->nChannels != 3)
00109     {
00110       cerr << "FindLEDSModule(): Eingabebild muß RGB-Bild sein!\n";
00111       throw bad_alloc();
00112     }
00113     img = cvCreateImage(cvSize(src.img->width, src.img->height), IPL_DEPTH_8U, 1);
00114     w = img->width;
00115     h = img->height;
00116   }
00117 
00118   ~FindLEDSModule()
00119   {
00120     cvReleaseImage(&img);
00121   }
00122 
00126   inline bool remarkable(const int x, const int y, const HSVRange& range)
00127   {
00128     int pix = getPixel8u_3(src.img, x, y);
00129     unsigned short pH, pS, pV;
00130     rgb2hsv(pix, &pH, &pS, &pV);
00131 
00132     if(pH>range.hmax) return false;
00133     if(pH<range.hmin) return false;
00134     if(pS>range.smax) return false;
00135     if(pS<range.smin) return false;
00136     if(pV>range.vmax) return false;
00137     if(pV<range.vmin) return false;
00138     return true;
00139   }
00140 
00145   inline void recursive_mark(const int x, const int y, const uchar m, const HSVRange& range)
00146   {
00147     putPixel8u_1(img, x, y, m);
00148     markierungen[m-1].p.x += x;
00149     markierungen[m-1].p.y += y;
00150     anzahl[m-1]++;
00151 
00152     if(x+1<w && getPixel8u_1(img, x+1, y)==0 && remarkable(x+1, y, range)) recursive_mark(x+1, y, m, range);
00153     if(y+1<h)
00154     {
00155       if(          getPixel8u_1(img, x  , y+1)==0 && remarkable(x, y+1, range)) recursive_mark(x, y+1, m, range);
00156       if(x+1<w  && getPixel8u_1(img, x+1, y+1)==0 && remarkable(x+1, y+1, range)) recursive_mark(x+1, y+1, m, range);
00157       if(x-1>=0 && getPixel8u_1(img, x-1, y+1)==0 && remarkable(x-1, y+1, range)) recursive_mark(x-1, y+1, m, range);
00158     }
00159   }
00160 
00161   inline void FloodFill(IplImage *img, myrect& ROI, unsigned short int& markiert, const HSVRange& range)
00162   {
00163     cvZero(img);// alte Markierungen löschen
00164     markiert = 0;
00165 
00166     for(int x=ROI.r.x; x<ROI.r.x+ROI.r.width; x++)
00167       for(int y=ROI.r.y; y<ROI.r.y+ROI.r.height; y++)
00168       {
00169         if(!remarkable(x, y, range)) continue;
00170         unsigned short int m = getPixel8u_1(img, x, y);
00171         if(m!=0) continue;
00172 
00173         // Womit ist rechts oder drunter markiert?
00174         if(x+1<w) m = getPixel8u_1(img, x+1, y);
00175         if(m==0 && y+1<h)
00176         {
00177                              m = getPixel8u_1(img, x  , y+1);
00178           if(m==0 && x+1<w)  m = getPixel8u_1(img, x+1, y+1);
00179           if(m==0 && x-1>=0) m = getPixel8u_1(img, x-1, y+1);
00180         }
00181 
00182         if(m==0)
00183         {
00184           // Neue Markierung vergeben
00185           if(markiert<MAXMARKS-1) markiert++;
00186           m = markiert;
00187           markierungen[m-1] = mypoint(0, 0);
00188           anzahl[m-1] = 0;
00189         }
00190         recursive_mark(x, y, m, range);
00191       } // for()
00192 
00193     if(DEBUGLEVEL>=3) cerr << "Vergebene Markierungen: " << markiert << endl;
00194 
00195     // Schwerpunktbildung: sum(x|y)/anzahl der pixel
00196     for(int i=0; i<markiert; i++)
00197     {
00198       markierungen[i].p.x /= anzahl[i];
00199       markierungen[i].p.y /= anzahl[i];
00200       if(DEBUGLEVEL>=3)
00201         cerr << "#" << i << " (" << markierungen[i].p.x << " " <<
00202           markierungen[i].p.y << ") " << anzahl[i] << " pix\n";
00203     }
00204   }
00205 
00206   void FloodFillMarks2Colors(IplImage *out)
00207   {
00208     for(int x=0; x<img->width; x++)
00209       for(int y=0; y<img->height; y++)
00210       {
00211         uchar m = getPixel8u_1(img, x, y);
00212         int n = 0;
00213         switch(m)
00214         {
00215           case 0: break;
00216           case 1: n = CV_RGB(255, 0, 0);break;// rot
00217           case 2: n = CV_RGB(0, 255, 0);break;// grün
00218           case 3: n = CV_RGB(0, 0, 255);break;// blau
00219           case 4: n = CV_RGB(255, 255, 0);break;// gelb
00220           case 5: n = CV_RGB(255, 0, 255);break;// magenta
00221           case 6: n = CV_RGB(0, 255, 255);break;// cyan
00222           case 7: n = CV_RGB(255, 0, 128);break;// pink
00223           case 8: n = CV_RGB(255, 128, 0);break;// orange
00224           case 9: n = CV_RGB(128, 0, 255);break;// violett
00225           case 10: n = CV_RGB(0, 128, 255);break;// hellblau
00226           default:
00227                   {
00228                     static int been_here=0;
00229                     if(!been_here)
00230                     {
00231                       been_here++;
00232                       cerr << "No more colors defined for marks>=" << int(m) << endl;
00233                     }
00234                   }
00235         } // switch
00236         putPixel8u_3(out, x, y, n);
00237       } // for y
00238   }
00239 
00243   virtual void processFrame(myrect *AROI)
00244   {
00245     if(!paramChanged && !src.outputChanged &&
00246        !corr.outputChanged && !range.outputChanged) return;
00247     if(AROI) ROI = *AROI;
00248     else ROI = myrect(0, 0, w, h);
00249 
00250     FloodFill(img, ROI, markiert, range);
00251 
00252     // -------------------------------------------------------------------------------
00253     // Erkennung auf Pixelebene abgeschlossen
00254     // -------------------------------------------------------------------------------
00255 
00256     // sehr nahe Schwerpunkte (<10 pixel, mindestabstand) zusammenwerfen
00257     int i,j;
00258     for(i=0; i<markiert; i++)
00259     {
00260       mypoint& s1 = markierungen[i];
00261       bool zusammengelegt;
00262       // Folgende while-Konstruktion existiert aufgrund fehlerhafter
00263       // Optimierung im gcc 3.2 (SuSE Prerelease) (Endlosschleife)
00264       // Bei "restart iteration of while" stand einst
00265       //        // "restart iteration of outer loop
00266       //        i--;break;
00267       do {
00268         zusammengelegt=false;
00269         for(j=i+1; j<markiert; j++)
00270         {
00271           if(anzahl[j] == 0) continue;// als gelöscht gekennzeichneter Schwerpunkt
00272           mypoint& s2 = markierungen[j];
00273           if(abs(s1-s2)<10)
00274           {
00275             if(DEBUGLEVEL>=3)
00276               cerr << "Lege #" << i << " und #" << j << " zusammen.\n";
00277             // neuen Schwerpunkt, gewichtet nach Pixelzahlen
00278             int alle = anzahl[i] + anzahl[j];
00279             float gi = anzahl[i]/(float)alle;
00280             float gj = anzahl[j]/(float)alle;
00281 
00282             markierungen[i].p.x = int(markierungen[i].p.x*gi + markierungen[j].p.x*gj);
00283             markierungen[i].p.y = int(markierungen[i].p.y*gi + markierungen[j].p.y*gj);
00284             anzahl[i] = alle;
00285 
00286             anzahl[j] = 0;
00287             //restart iteration of while
00288             zusammengelegt=true;
00289             break;
00290           } // if
00291         } // for
00292       } while(zusammengelegt==true);
00293     }
00294 
00295     // ungefähre Anzahl der Pixel berechnen, die eine LED im Bild hat
00296     // Gemessen wurde:
00297     //
00298     //  Modell | Radradius in Pixeln | Anzahl der Pixel pro LED
00299     //  -------+---------------------+-------------------------
00300     //  Adam   | 104                 | 55
00301     //         |  71                 | 2,5,6,11,14
00302     //  Eva    |  68                 | 1-10
00303     //         |                     | 2-20
00304     //         |                     | 3-15
00305     // Ansatz: Radradius ~ LED-Fläche = PI*radius_LED^2
00306     //         radius_LED = radradius*c (c wurde aus obiger Tabelle abgeleitet: 0.045 < c < 0.068)
00307     int pixPerLED = int(sqr(micro.radius*0.07));
00308 
00309     int MaxPixPerLED = int(pixPerLED*1.4);
00310     int MinPixPerLED = int(pixPerLED*0.1);
00311     if(DEBUGLEVEL>=3) cerr << "Pixel pro LED: ideal=" << pixPerLED
00312       << " min=" << MinPixPerLED
00313       << " max=" << MaxPixPerLED << endl;
00314 
00315     // Schwerpunkte mit >MaxPixPerLED (früher war es fest >50) oder <MinPixPerLED Pixeln rauswerfen
00316     for(int i=0; i<markiert; i++)
00317     {
00318       if(anzahl[i] > MaxPixPerLED) anzahl[i] = 0;
00319       if(anzahl[i] < MinPixPerLED) anzahl[i] = 0;
00320     }
00321 
00322     // Abstand zum Radmittelpunkt berechnen (r)
00323     if(!micro.mitteValid && DEBUGLEVEL>=1) cerr << "Radmitte unbekannt :(\n";
00324     if(!micro.radiusValid && DEBUGLEVEL>=1) cerr << "Radius unbekannt :(\n";
00325 
00326     const float LED_MINABSTAND = 0.1;
00327     const float LED_MAXABSTAND = 0.8;
00328     for(int i=0; i<markiert; i++)
00329     {
00330       if(anzahl[i] == 0) continue;// als gelöscht gekennzeichneter Schwerpunkt
00331       mypoint& s1 = markierungen[i];
00332       s1 = s1 - micro.mitte;
00333       abstand[i] = abs(s1)/micro.radius;
00334 
00335       // Schwerpunkte mit zu großen/zu kleinem Abstand vom Mittelpunkt
00336       // rauswerfen, da sie sich nur auf einem Ring darum befinden können
00337       if(abstand[i] > LED_MAXABSTAND) anzahl[i] = 0;
00338       if(abstand[i] < LED_MINABSTAND) anzahl[i] = 0;
00339       //cerr << "Abstand zur Radmitte #" << i << ": " << abstand[i] << endl;
00340     }
00341 
00342     // Schwerpunkte mit der Anzahl der Pixel gewichten
00343     // zuviele = schlecht, zuwenige auch
00344     // auch müssen die Schwerpkt. einen Mindestabstand voneinander
00345     // aufweisen
00346     // TODO
00347 
00348     // Gamma, Winkel zwischen positiver x-Achse und LED-Position,
00349     // relativ zur Radmitte berechnen
00350     // Inverse Funktion von:
00351     //  x = cos(gamma)*r
00352     //  y = sin(gamma)*r,
00353     // sowie Berechnung von beta und gamma0, welches der LED-Position bei am Rand anliegendem
00354     // Arm entspricht: gamma = gamma0 + beta
00355     // beta ~ 1/r
00356     for(int i=0; i<markiert; i++)
00357     {
00358       if(anzahl[i] == 0) continue;// als gelöscht gekennzeichneter Schwerpunkt
00359       gamma[i] = acos((markierungen[i].p.x) / (abstand[i]*micro.radius));
00360       // da 0 <= acos() <= M_PI:
00361       if(markierungen[i].p.y > 0) // < was for eva
00362       gamma[i] = 2*M_PI - gamma[i];
00363 
00364       // 0 <= beta <= 1
00365       // wobei 0 bedeutet: Arm liegt am Rad außen an
00366       // und 1 bedeutet: Arm ist maximal nach innen zur Radmitte bewegt
00367       beta[i] = 1-(abstand[i]-LED_MINABSTAND)/(LED_MAXABSTAND-LED_MINABSTAND);
00368 
00369       // Als Eva-Armansatzpunkt wird die LED-Position bei am Rad außen
00370       // anliegendem Arm betrachtet
00371       // Ist der Arm maximal ausgefahren, also beta==1, so
00372       // erscheint die LED schätzungsweise um 10 Grad im Gegenurzeigersinn
00373       // vom Armansatzpunkt bei beta==0
00374       gamma0[i] = gamma[i] + beta[i]*20/180*M_PI;
00375       //cerr << "#" << i << " gamma=" << gamma[i]*180/M_PI << "° gamma0="
00376       //  << gamma0[i]*180/M_PI << "° beta=" << int(beta[i]*100) << "%\n";
00377     }
00378 
00379     if(eva)
00380     {
00381       // -------------------------------------------------------------------------------
00382       // Erkennung der LED von micro.Eva
00383       // -------------------------------------------------------------------------------
00384 
00385       // Die Markierungen die auswählen, die am besten ins 72°-Raster passen
00386       // per Fehlerminimierung
00387       for(int z=0; z<markiert; z++) gewaehlt[z] = 0;
00388       float fehler1min = 1e6;
00389 
00390       for(int y=0; y<markiert; y++)
00391       {
00392         if(anzahl[y] == 0) continue;
00393 
00394         // jeden Schwerpunkt einmal als Ausgangspunkt merken
00395         for(int z=0; z<markiert; z++)
00396         {
00397           gemerkt[z] = 0;
00398         }
00399         gemerkt[y] = 1;// das ist Arm #1
00400 
00401         float fehler1temp = 0;
00402         for(int i=1; i<5; i++) // je ein gamma0 Arm #2-5 zuordnen
00403         {
00404           // wo wäre der nächste Arm zu finden?
00405           float igamma0 = gamma0[y] + i*72./360.*2.*M_PI;
00406           if(igamma0 > 2*M_PI) igamma0 -= 2.*M_PI;
00407           float fehler2min = 50./360.*2.*M_PI;
00408           //    cerr << "fehler2min=" << fehler2min << endl;
00409           int minx = -1;
00410           for(int x=0; x<markiert; x++) // am besten passenden Schwerpunkt finden
00411           {
00412             if(anzahl[x] == 0) continue;
00413             if(gemerkt[x]) continue;
00414             float adiff =  anglediff(igamma0, gamma0[x]);
00415             if(adiff<fehler2min)
00416             {
00417               //cerr << "igamma0=" << igamma0 << " gamma0[x]=" << gamma0[x]
00418               //  << " fehler2min=" << adiff << endl;
00419               fehler2min = adiff;
00420               minx = x;
00421             }
00422           }
00423           if(minx==-1)
00424             // kein passender Schwerpunkt gefunden
00425             fehler1temp += 100;
00426           else
00427           {
00428             fehler1temp += fehler2min;
00429             gemerkt[minx] = i+1;// das ist Arm #i+1
00430           }
00431         }
00432         if(fehler1temp<fehler1min)
00433         {
00434           fehler1min = fehler1temp;
00435           //cerr << " fehler1min=" << fehler1min << endl;
00436           memcpy(gewaehlt, gemerkt, sizeof(gewaehlt));
00437         }
00438       }
00439       // in gewaehlt[] stehen nun die Indizes der max. 5 Schwerpunkte,
00440       // die am besten auf Armpositionen passen
00441 
00442       // alpha berechnen
00443       // finde das i, für das min(gamma0[i], alpha)
00444       // auch falls !alphaValid, kann Zuordnung so erfolgen
00445       // da dann egal ist, welches i gewinnt
00446       float fehler3=10;
00447       int alphai = -1;
00448       for(int i=0; i<markiert; i++)
00449       {
00450         if(!gewaehlt[i]) continue;
00451         float adiff = anglediff(micro.alpha, gamma0[i]);
00452         if(fehler3 > adiff)
00453         {
00454           alphai = i;
00455           fehler3 = adiff;
00456         }
00457       }
00458       if(fehler3<10)
00459       {
00460         micro.alpha = gamma[alphai];
00461         micro.alphaValid = true;
00462       }
00463 
00464       if(alphai==-1)
00465       {
00466         if(DEBUGLEVEL>=1)
00467           cerr << "Kein Schwerpunkt gefunden -> kein alpha.\n";
00468       }
00469       else
00470       {
00471         // Zuordnen der Arm# in gewaehlt[]
00472         // zu den richtigen beta[]s
00473         // Es ist eine Rotation der in gewaehlt[] gespeicherten Indizes
00474         // um ga Positionen nach links (bzw. 5-ga nach rechts), z.B. sei ga=3
00475         // gewaehlt[]:  1 2 3 4 5
00476         // armnummer[]: 4 5 1 2 3
00477         // alphai ist der "neue" Arm#1
00478         char ga = gewaehlt[alphai];// alte Arnmummer, die nun Arm #1 wird
00479         eva->beta[0] = beta[alphai];
00480         armnummer[alphai] = 1;
00481         //cerr << "alphai=" << alphai << " alt=" << int(ga) << " neu: 1!" << endl;
00482         for(int i=0; i<markiert; i++)
00483         {
00484           char gi = gewaehlt[i];// alte Armnummer
00485           if(gi<1)
00486           {
00487             armnummer[i] = 0;
00488             continue;
00489           }
00490           if(gi==ga) continue;// schon vor der Schleife behandelt
00491           int an = 1+((gi+(5-ga)) % 5);
00492           eva->beta[an-1] = beta[i];
00493           armnummer[i] = an;
00494           //cerr << " i=" << i << " alt=" << int(gi) << " neu=" << an << endl;
00495         }
00496         //cerr << endl;
00497       }
00498     }
00499     else
00500     // -------------------------------------------------------------------------------
00501     // Erkennung der LED von micro.Adam
00502     // -------------------------------------------------------------------------------
00503     {
00504       MicroAdam *adam = dynamic_cast<MicroAdam*>(&micro);
00505       if(adam)
00506       {
00507         // alpha += 180°, wenn gamma[LED] nahe alpha+beta liegt
00508 
00509         int ledIndex=-1;
00510         for(int i=0; i<markiert; i++)
00511         {
00512           // gelöschter Schwerpunkt? weiter!
00513           if(anzahl[i] == 0) continue;
00514           // Schwerpunkt zu nah am Mittelpunkt? Kann nicht sein!
00515           if(abstand[i]<0.55)
00516           {
00517             anzahl[i]=0; continue;
00518           }
00519           float d = anglediff(gamma[i], micro.alpha+adam->beta);
00520           if(d > 10./180.*M_PI) continue;
00521 
00522           // if more than 1 LED is found, situation
00523           // is ambiguous and we don't try to correct alpha
00524           if(ledIndex!=-1)
00525           {
00526             ledIndex=-1;
00527             break;// leave for()
00528           }
00529           ledIndex=i;
00530           //cerr << "Remembering Index: " << ledIndex;
00531         }
00532         if(ledIndex!=-1)
00533         {
00534           if(anglediff(gamma[ledIndex], micro.alpha+adam->beta)
00535               < 10./180.*M_PI)
00536           {
00537             if(DEBUGLEVEL>=2)
00538               cerr << "Switching alpha by 180°: gamma[]=" << gamma[ledIndex]
00539                 << " alpha=" << micro.alpha << "\n";
00540             micro.alpha += M_PI;
00541           }
00542         }
00543       }
00544     }
00545 
00546     if(DEBUGLEVEL>=3) cerr << "<-FindLEDS\n";
00547     paramChanged = false;
00548     range.outputChanged = false;
00549     outputChanged = true;
00550   }
00551 
00552   virtual void processKeyFrame(void)
00553   {
00554     if(DEBUGLEVEL>=3) cerr << "FindLEDSModule::processKeyFrame()\n";
00555     processFrame(NULL);
00556   }
00557 
00558 };
00559 
00560 }
00561 
00562 #endif // FINDLEDSMODULE_H
00563 

Erzeugt am Sun Oct 3 12:52:47 2004 für seemicro von doxygen 1.3.2