Suche einen Algorithmus zum entfernen des Fischaugeneffekts
-
Also ich habe schonmal folgende nährungsweise Kalibierung erfolgreich verwendet:
Kappa musste aber von Hand für jedes Objektiv bestimmen. Zum Beispiel unter Verwendung von Karopapier: Solage justieren bis alle Linien, Geraden sind.
-
@Hirsch_H
Danke erstmal für deinen neuen Ansatz.
Ich habe aber noch zwei Fragen dazu:
-
Das r entspricht dem Abstand zum Bildmittelpunkt?
-
In welcher Größenordnung war denn das Kappa bei dir? Ist das ein sehr kleiner oder sehr großer Wert?
-
-
Er meinte das ich die 22mm ins Verhältnis zur Bild größe setzen soll. Sofern ich das richtig verstanden habe...
Du rechnest mit verschiedenen Groessen, naemlich "r" (Abstand vom Mittelpunkt) in Pixel und "rk" (entspricht ungefaehr der Brennweite) in mm. Du musst also eins von beiden ins andere umrechnen.
Die Brennweite ist der Punkt wo sich alle Strahlen treffen, die durch die Oberflaeche der Linse verlaufen. Du kannst also zwischen Schnittpunkt, Bogen und aeusseren Strahlen ein gleichschenkliges Dreieck bilden und die Laenge der fehlenden Kante (als Annaeherung der Bildgroesse auf der Linsenoberflaeche) berechnen. Da der CCD-Sensor aber auch nicht punktfoermig ist, wird das so oder so nur eine Naeherung. Du wirst das tatsaechliche "rk" also nur durch Ausprobieren (Anhand eines geeigneten Testbildes) ermitteln koennen.
-
Denis103 schrieb:
@Hirsch_H
Danke erstmal für deinen neuen Ansatz.
Ich habe aber noch zwei Fragen dazu:
-
Das r entspricht dem Abstand zum Bildmittelpunkt?
-
In welcher Größenordnung war denn das Kappa bei dir? Ist das ein sehr kleiner oder sehr großer Wert?
zu 1) Ja, r ist der Abstand zum Mittelpunkt. Musst halt noch die koordinaten in den Mittelpunkt verschieben.
zu 2) Kappa ist ziemlich klein, um die 1e8 kannste noch multiplizieren um es handhabbar zu machen.
-
-
@ __HIRSCH_H__
Könnte es sein, dass dieser Algorithmus zum erzeugen eines Fischaugeneffektes dient, anstatt ihn zu entfernen? Nach dem Verschieben des Koordinatenursprungs (Bildmitte ist nun (0,0)) und einigen Tests wird der Effekt bei mir immer nur verstärkt, anstatt ihn zu beseitigen. Die Ecken werden also mehr gestaucht als die Mitten der Bildränder. Zum entfernen müsste dies ja genau andersherum sein. Die Ecken bleiben wo sie sind und die Pixel an den Bildränder rücken Richtung Bildzentrum.
-
Versuchs mal mit negativem Kappa
-
@ __HIRSCH_H__
Es lag am Vorzeichen. Bei postiven Kappa zieht er es auseinander und die Kurven werden zu Geraden und bei negativen Kappa erzeugt er diesen Fischaugeneffekt.
Ich bin aber nicht so ganz zufrieden damit, weil das Bild gezogen wird, wodurch Stellen entstehen an denen keine Pixel mehr vorhanden sind. Gibts dafür noch eine Lösung? Vielleicht eine Art Filter?
-
Du berechnest vorwärts? Rechne lieber rückwärts, ähnlich wie beim texture-mapping.
Wenn Du wissen willst welche Farbe an der Stelly (x,y) in deinem Bild sein muß, dann verzerre es fischaugen-mäßig zu (x',y') und schau die Farbe dann im Fischaugenbild nach. Dadurch kriegste für jedes Pixel ne Farbinformation und nicht für manche mehrere und für manche garkeine.
-
Wie meinst du das?
Du berechnest vorwärts? Rechne lieber rückwärts
Ich rechne nach den zwei Formeln. Wie gebe ich da eine Richtung an?
-
Denis103 schrieb:
@ __HIRSCH_H__
Es lag am Vorzeichen. Bei postiven Kappa zieht er es auseinander und die Kurven werden zu Geraden und bei negativen Kappa erzeugt er diesen Fischaugeneffekt.
Ich bin aber nicht so ganz zufrieden damit, weil das Bild gezogen wird, wodurch Stellen entstehen an denen keine Pixel mehr vorhanden sind. Gibts dafür noch eine Lösung? Vielleicht eine Art Filter?
Genau wie Jester gesagt hat, rechne rückwärts, das heisst du musst dein Zielbild sampeln, nicht das Ausgangsbild. Die Farben für das Zielbild kannste dann mit einer beliebigen Funktion (nearest-neigbour, linear oder quadratisch) aus den berechneten float Koordinaten interpolieren.
-
Denis103 schrieb:
Wie meinst du das?
Du berechnest vorwärts? Rechne lieber rückwärts
Ich rechne nach den zwei Formeln. Wie gebe ich da eine Richtung an?
Ein codebeispiel:
const int nW2 = from.width()/2; const int nH2 = from.height()/2; for(int nY=0; nY<to.height(); ++nY) { const int nYC = nY - nH2; for(int nX=0; nX<to.width(); ++nX) { const int nXC = nX - nW2; const float fR = nXC*nXC + nYC*nYC; const float fX = nXC/(1.0f+fKappa*fR) + nW2; const float fY = nYC/(1.0f+fKappa*fR) + nH2; if (fX >= 0 && fX < from.width() && fY >= 0 && fY < from.height()) to(nX, nY) = interpolate(fX, fY); } }
-
ich mach das so:
:: Ist ne BCB- Variante !!! (VCL)
:: ist auf 24Bit Farbtiefe getrimmt !!!
:: Interpolator ist entfernt !!!:: Berechnet 4 verschiedene FishEye- Geometrien wahlweise
:: Kann das Bild dabei Skalieren
Die Bildabmessungen werden gehalten, nur der Inhalt wird "gezoomt":: Zu jedem Punkt im ERGEBNIS- Bild wird die Koordinate im AUSGANGS- Bild
berechnet und Der Farbwert von dort geholt
Die Berechnungen erfolgen deshalb mit double, um vernünftig interpolieren
zu können.Die Funktion ist in einem fertigen Programm integriert und klappt !!
(Da ist dann allerdings eine Interpolation drin, die abhängig von der Perspektive jedes Pixel passend berechnet)Die Formeln sind gemäss Prof. Dersch !!!
(siehe vorigen Link)//Funktion zum Entzerren von FishEye- Bildern //Parameter: //Originalbild //Zielbild //Brennweite //Cropfaktor //Skalierfaktor //FishEye- Methode void __fastcall FishEyePicNoInterpolate(Graphics::TBitmap *OrgBM,Graphics::TBitmap *DestBM,double Aperture, double LensFactor, double FishEyeZoom, int FishEyeMode) { double FCorrVal=1; FCorrVal=FCorrVal * max(OrgBM->Height,OrgBM->Width) / 36.0; // FCorrVal=FCorrVal * min(OrgBM->Height,OrgBM->Width) / 24.0; double F=Aperture * FCorrVal * LensFactor; int ImgW=OrgBM->Width; int ImgH=OrgBM->Height; double ImgW_2=ImgW / 2.0; double ImgH_2=ImgH / 2.0; double RBQ,RB; double ROrg; TPoint MP; MP.x=(ImgW - 1) / 2.0;// + 1; MP.y=(ImgH - 1) / 2.0;// + 1; int X,Y; int XOrg,YOrg; double XOrgDbl,YOrgDbl,XSrcDbl,YSrcDbl; PRGBTriple Row; PRGBTriple OrgRow; PRGBTriple *POrgRow=NULL; DestBM->Width=ImgW; DestBM->Height=ImgH; DestBM->PixelFormat=pf24bit; OrgBM->PixelFormat=pf24bit; POrgRow=(PRGBTriple*)malloc(sizeof(PRGBTriple) * ImgH); if (POrgRow!=NULL) { for (int Zeile=0;Zeile<ImgH;Zeile++) POrgRow[Zeile]=(PRGBTriple)OrgBM->ScanLine[Zeile]; try { for (Y=0;Y<ImgH;Y++) { Row=(PRGBTriple)DestBM->ScanLine[Y]; YSrcDbl=ImgH_2 - Y; for (X=0;X<ImgW;X++) { XSrcDbl=-ImgW_2 + X; XOrgDbl=XSrcDbl; YOrgDbl=YSrcDbl; XOrgDbl=XOrgDbl / FishEyeZoom; YOrgDbl=YOrgDbl / FishEyeZoom; RBQ=XOrgDbl * XOrgDbl + YOrgDbl * YOrgDbl; RB=sqrt(RBQ); switch (FishEyeMode) { case 0: ROrg=2 * F * sin(atan(RB / F) / 2); if (RB>0) { XOrgDbl=XOrgDbl * ROrg / RB; YOrgDbl=YOrgDbl * ROrg / RB; } break; case 1: ROrg=F * atan(RB / F); if (RB>0) { XOrgDbl=XOrgDbl * ROrg / RB; YOrgDbl=YOrgDbl * ROrg / RB; } break; case 2: ROrg=F * sin(atan(RB / F)); if (RB>0) { XOrgDbl=XOrgDbl * ROrg / RB; YOrgDbl=YOrgDbl * ROrg / RB; } break; case 3: ROrg=2 * F * tan(atan(RB / F) / 2); if (RB>0) { XOrgDbl=XOrgDbl * ROrg / RB; YOrgDbl=YOrgDbl * ROrg / RB; } break; } YOrgDbl=ImgH_2 - YOrgDbl; XOrgDbl=ImgW_2 + XOrgDbl; YOrg=int(YOrgDbl); if ((YOrg<0)||(YOrg>=ImgH)) continue; OrgRow=POrgRow[YOrg]; XOrg=XOrgDbl; if (XOrgDbl<0) XOrgDbl=0; if (YOrgDbl<0) YOrgDbl=0; if ((XOrg>=0)&&(XOrg<ImgW)) { //Hier sollte eine Interpolation stattfinden !!! Row[X].rgbtRed=OrgRow[XOrg].rgbtRed; Row[X].rgbtGreen=OrgRow[XOrg].rgbtGreen; Row[X].rgbtBlue=OrgRow[XOrg].rgbtBlue; } } } } catch(...) { Application->MessageBox("Sie haben ungültige Werte gewählt\r\nDie Berechnung ist nicht möglich.\r\n\r\nErhöhen Sie die Brennweite oder\r\nsetzen Sie den Horizont mehr zur Bildmitte.","Geometriefehler!!!",MB_OK + MB_ICONEXCLAMATION); } free(POrgRow); } } //--------------------------------------------------------------------------- //Aufruf der Funktion void __fastcall DeFishEyeClick(TObject *Sender) { FishEyePicNoInterpolate(Origin->Bitmap, //Ausgangsbild Recti->Picture->Bitmap, //Entzerrtes Bild FL->Caption.ToDouble(), //Brennweite 1, //Crop- Faktor (Standard sollte 1 sein) 1, //Zoom- faktor (Vergrösserungsfaktor = beliebig, positiv) 1); //FishEye- Modus (0 .. 3) }
Origin und Recti sind BCB- TImage elemente, die die Graphic als TBitmap kapseln.
TBitmap hat Scanline zum schnellen Zugriff auf Pixel- Zeile
-
Das klappt rückwärts super. Jetzt verstehe ich auch wie ihr das meintet.
Habt nochmal vielen Dank für eure tolle Unterstützung!