Frage

UPDATE DES BOUNTY-STATUS:

Ich habe herausgefunden, wie man eine lineare Linse abbildet, aus destination Koordinaten zu source Koordinaten.

Wie berechnet man den radialen Abstand vom Mittelpunkt, um vom Fischauge zum geradlinigen Bild zu gelangen?

  • 1). Es fällt mir tatsächlich schwer, es umzukehren und die Quellkoordinaten den Zielkoordinaten zuzuordnen.Was ist das Gegenteil im Code im Stil der von mir geposteten Konvertierungsfunktionen?

  • 2). Ich sehe auch, dass meine Verzeichnung bei manchen Objektiven nicht perfekt ist – vermutlich bei solchen, die nicht streng linear sind.Was sind die äquivalenten Quell- und Zielkoordinaten für diese Linsen?Nochmals, bitte mehr Code als nur mathematische Formeln ...


Frage wie ursprünglich gestellt:

Ich habe einige Punkte, die Positionen in einem Bild beschreiben, das mit einem Fischaugenobjektiv aufgenommen wurde.

Ich möchte diese Punkte in geradlinige Koordinaten umwandeln.Ich möchte das Bild entzerren.

Ich habe gefunden diese Beschreibung Es geht darum, wie man einen Fischaugeneffekt erzeugt, aber nicht, wie man ihn umkehrt.

Es gibt auch ein Blogeintrag das beschreibt, wie man dafür Werkzeuge verwendet;Diese Bilder stammen davon:

(1) : SOURCE Original-Fotolink

Eingabe: Originalbild mit zu korrigierender Fischaugenverzerrung.

(2) : DESTINATION Original-Fotolink

Ausgabe : Korrigiertes Bild (technisch gesehen auch mit perspektivischer Korrektur, aber das ist ein separater Schritt).

Wie berechnet man den radialen Abstand vom Mittelpunkt, um vom Fischauge zum geradlinigen Bild zu gelangen?

Mein Funktionsstub sieht so aus:

Point correct_fisheye(const Point& p,const Size& img) {
    // to polar
    const Point centre = {img.width/2,img.height/2};
    const Point rel = {p.x-centre.x,p.y-centre.y};
    const double theta = atan2(rel.y,rel.x);
    double R = sqrt((rel.x*rel.x)+(rel.y*rel.y));
    // fisheye undistortion in here please
    //... change R ...
    // back to rectangular
    const Point ret = Point(centre.x+R*cos(theta),centre.y+R*sin(theta));
    fprintf(stderr,"(%d,%d) in (%d,%d) = %f,%f = (%d,%d)\n",p.x,p.y,img.width,img.height,theta,R,ret.x,ret.y);
    return ret;
}

Alternativ könnte ich das Bild irgendwie vom Fischaugenbild in ein geradliniges Bild umwandeln, bevor ich die Punkte finde, aber das verwirrt mich völlig OpenCV-Dokumentation.Gibt es eine einfache Möglichkeit, dies in OpenCV zu tun, und ist die Leistung gut genug, um es in einem Live-Video-Feed durchzuführen?

War es hilfreich?

Lösung

Das Beschreibung, die Sie erwähnen gibt an, dass die Projektion durch eine Pin-Loch-Kamera (eine, die keine Linsenverzerrung einführt) von modelliert wird

R_u = f*tan(theta)

und die Projektion durch gemeinsame Fischaugen -Objektivkameras (dh verzerrt) wird von modelliert

R_d = 2*f*sin(theta/2)

Sie kennen R_D und Theta bereits und wenn Sie die Brennweite der Kamera (dargestellt durch F) kennen, würde das Korrigieren des Bildes in Bezug auf R_D und Theta die Berechnung von R_U bedeuten. Mit anderen Worten,

R_u = f*tan(2*asin(R_d/(2*f)))

ist die Formel, nach der Sie suchen. Die Schätzung der Brennweite F kann gelöst werden, indem die Kamera oder andere Mittel kalibriert werden, z. B. den Benutzer Feedback geben, wie gut das Bild korrigiert wird oder Wissen aus der ursprünglichen Szene verwendet.

Um das gleiche Problem mit OpenCV zu lösen, müssten Sie die intrinsischen Parameter und die Objektivverzerrungskoeffizienten der Kamera erhalten. Siehe zum Beispiel Kapitel 11 von Lernen opencv (Vergessen Sie nicht, das zu überprüfen Korrektur). Dann können Sie ein Programm wie dieses (geschrieben mit den Python -Bindungen für OpenCV) verwenden, um die Linsenverzerrung umzukehren:

#!/usr/bin/python

# ./undistort 0_0000.jpg 1367.451167 1367.451167 0 0 -0.246065 0.193617 -0.002004 -0.002056

import sys
import cv

def main(argv):
    if len(argv) < 10:
    print 'Usage: %s input-file fx fy cx cy k1 k2 p1 p2 output-file' % argv[0]
    sys.exit(-1)

    src = argv[1]
    fx, fy, cx, cy, k1, k2, p1, p2, output = argv[2:]

    intrinsics = cv.CreateMat(3, 3, cv.CV_64FC1)
    cv.Zero(intrinsics)
    intrinsics[0, 0] = float(fx)
    intrinsics[1, 1] = float(fy)
    intrinsics[2, 2] = 1.0
    intrinsics[0, 2] = float(cx)
    intrinsics[1, 2] = float(cy)

    dist_coeffs = cv.CreateMat(1, 4, cv.CV_64FC1)
    cv.Zero(dist_coeffs)
    dist_coeffs[0, 0] = float(k1)
    dist_coeffs[0, 1] = float(k2)
    dist_coeffs[0, 2] = float(p1)
    dist_coeffs[0, 3] = float(p2)

    src = cv.LoadImage(src)
    dst = cv.CreateImage(cv.GetSize(src), src.depth, src.nChannels)
    mapx = cv.CreateImage(cv.GetSize(src), cv.IPL_DEPTH_32F, 1)
    mapy = cv.CreateImage(cv.GetSize(src), cv.IPL_DEPTH_32F, 1)
    cv.InitUndistortMap(intrinsics, dist_coeffs, mapx, mapy)
    cv.Remap(src, dst, mapx, mapy, cv.CV_INTER_LINEAR + cv.CV_WARP_FILL_OUTLIERS,  cv.ScalarAll(0))
    # cv.Undistort2(src, dst, intrinsics, dist_coeffs)

    cv.SaveImage(output, dst)


if __name__ == '__main__':
    main(sys.argv)

Beachten Sie auch, dass OpenCV ein ganz anderes Objektivverzerrungsmodell als das auf der Webseite verwendet, mit dem Sie verlinkt sind.

Andere Tipps

(Originalplakat, die eine Alternative liefert)

Die folgenden Funktionen sind Koordinaten für die Quelle (fisheye-distorierte) Koordinaten. (Ich würde mich freuen, es umzukehren, um es umzukehren)

Ich habe diesen Punkt durch Versuch und Irrtum erreicht: Ich verstehe nicht grundlegend, warum dieser Code funktioniert, Erklärungen und verbesserte Genauigkeit geschätzt!

def dist(x,y):
    return sqrt(x*x+y*y)

def correct_fisheye(src_size,dest_size,dx,dy,factor):
    """ returns a tuple of source coordinates (sx,sy)
        (note: values can be out of range)"""
    # convert dx,dy to relative coordinates
    rx, ry = dx-(dest_size[0]/2), dy-(dest_size[1]/2)
    # calc theta
    r = dist(rx,ry)/(dist(src_size[0],src_size[1])/factor)
    if 0==r:
        theta = 1.0
    else:
        theta = atan(r)/r
    # back to absolute coordinates
    sx, sy = (src_size[0]/2)+theta*rx, (src_size[1]/2)+theta*ry
    # done
    return (int(round(sx)),int(round(sy)))

Wenn es mit einem Faktor 3.0 verwendet wird, wird die als Beispiele verwendeten Bilder erfolgreich nicht aufgestellt (ich habe keinen Versuch der Qualitätsinterpolation unternommen):

Dead Link

(Und das ist aus dem Blog -Beitrag zum Vergleich :)

Using Panotools

Wenn Sie der Meinung sind, dass Ihre Formeln genau sind, können Sie eine genaue Formel mit Trig haben, wie so:

Rin = 2 f sin(w/2) -> sin(w/2)= Rin/2f
Rout= f tan(w)     -> tan(w)= Rout/f

(Rin/2f)^2 = [sin(w/2)]^2 = (1 - cos(w))/2  ->  cos(w) = 1 - 2(Rin/2f)^2
(Rout/f)^2 = [tan(w)]^2 = 1/[cos(w)]^2 - 1

-> (Rout/f)^2 = 1/(1-2[Rin/2f]^2)^2 - 1

Wie @JMBR sagt, hängt die tatsächliche Kameraverzerrung jedoch vom Objektiv und dem Zoom ab. Anstatt sich auf eine feste Formel zu verlassen, möchten Sie möglicherweise eine Polynomausdehnung ausprobieren:

Rout = Rin*(1 + A*Rin^2 + B*Rin^4 + ...)

Wenn Sie zuerst die Koeffizienten höherer Ordnung optimieren, können Sie jede angemessene lokale Funktion berechnen (die Form der Expansion nutzt die Symmetrie des Problems aus). Insbesondere sollte es möglich sein, Anfangskoeffizienten zu berechnen, um die oben theoretische Funktion zu approximieren.

Für gute Ergebnisse müssen Sie auch einen Interpolationsfilter verwenden, um Ihr korrigiertes Bild zu generieren. Solange die Verzerrung nicht allzu groß ist, können Sie die Art von Filter verwenden, mit der Sie das Bild linear ohne viel Problem neu scalen würden.

Bearbeiten: Gemäß Ihrer Anfrage ist der äquivalente Skalierungsfaktor für die obige Formel:

(Rout/f)^2 = 1/(1-2[Rin/2f]^2)^2 - 1
-> Rout/f = [Rin/f] * sqrt(1-[Rin/f]^2/4)/(1-[Rin/f]^2/2)

Wenn Sie die obige Formel neben Tan (Rin/F) zeichnen, können Sie feststellen, dass sie sehr ähnlich sind. Grundsätzlich wird die Verzerrung der Tangente schwerwiegend, bevor die Sünde (W) sich stark von w unterscheidet.

Die inverse Formel sollte so etwas sein wie:

Rin/f = [Rout/f] / sqrt( sqrt(([Rout/f]^2+1) * (sqrt([Rout/f]^2+1) + 1) / 2 )

Ich habe die Formeln blind implementiert hier, Ich kann also nicht garantieren, dass es das tun würde, was Sie brauchen.

Verwenden auto_zoom Um den Wert für die zu erhalten zoom Parameter.


def dist(x,y):
    return sqrt(x*x+y*y)

def fisheye_to_rectilinear(src_size,dest_size,sx,sy,crop_factor,zoom):
    """ returns a tuple of dest coordinates (dx,dy)
        (note: values can be out of range)
 crop_factor is ratio of sphere diameter to diagonal of the source image"""  
    # convert sx,sy to relative coordinates
    rx, ry = sx-(src_size[0]/2), sy-(src_size[1]/2)
    r = dist(rx,ry)

    # focal distance = radius of the sphere
    pi = 3.1415926535
    f = dist(src_size[0],src_size[1])*factor/pi

    # calc theta 1) linear mapping (older Nikon) 
    theta = r / f

    # calc theta 2) nonlinear mapping 
    # theta = asin ( r / ( 2 * f ) ) * 2

    # calc new radius
    nr = tan(theta) * zoom

    # back to absolute coordinates
    dx, dy = (dest_size[0]/2)+rx/r*nr, (dest_size[1]/2)+ry/r*nr
    # done
    return (int(round(dx)),int(round(dy)))


def fisheye_auto_zoom(src_size,dest_size,crop_factor):
    """ calculate zoom such that left edge of source image matches left edge of dest image """
    # Try to see what happens with zoom=1
    dx, dy = fisheye_to_rectilinear(src_size, dest_size, 0, src_size[1]/2, crop_factor, 1)

    # Calculate zoom so the result is what we wanted
    obtained_r = dest_size[0]/2 - dx
    required_r = dest_size[0]/2
    zoom = required_r / obtained_r
    return zoom

Ich habe das, was JMBR gemacht hat, genommen und es im Grunde umgekehrt gemacht.Er nahm den Radius des verzerrten Bildes (Rd, also den Abstand in Pixeln von der Bildmitte) und fand eine Formel für Ru, den Radius des unverzerrten Bildes.

Du willst in die andere Richtung gehen.Für jedes Pixel im unverzerrten (verarbeiteten Bild) möchten Sie wissen, um welches Pixel es sich im verzerrten Bild handelt.Mit anderen Worten: gegeben (xu, yu) -> (xd, yd).Anschließend ersetzen Sie jedes Pixel im unverzerrten Bild durch das entsprechende Pixel aus dem verzerrten Bild.

Ich beginne mit JMBR und gehe umgekehrt vor, indem ich Rd als Funktion von Ru finde.Ich bekomme:

Rd = f * sqrt(2) * sqrt( 1 - 1/sqrt(r^2 +1))

wobei f die Brennweite in Pixel ist (ich erkläre es später) und r = Ru/f.

Die Brennweite meiner Kamera betrug 2,5 mm.Die Größe jedes Pixels auf meinem CCD betrug 6 um im Quadrat.f war also 2500/6 = 417 Pixel.Dies kann durch Versuch und Irrtum gefunden werden.

Mit „Find Rd“ können Sie mithilfe von Polarkoordinaten das entsprechende Pixel im verzerrten Bild finden.

Der Winkel jedes Pixels vom Mittelpunkt ist gleich:

theta = arctan( (yu-yc)/(xu-xc) ) wobei xc, yc die Mittelpunkte sind.

Dann,

xd = Rd * cos(theta) + xc
yd = Rd * sin(theta) + yc

Stellen Sie sicher, dass Sie wissen, in welchem ​​Quadranten Sie sich befinden.

Hier ist der C#-Code, den ich verwendet habe

 public class Analyzer
 {
      private ArrayList mFisheyeCorrect;
      private int mFELimit = 1500;
      private double mScaleFESize = 0.9;

      public Analyzer()
      {
            //A lookup table so we don't have to calculate Rdistorted over and over
            //The values will be multiplied by focal length in pixels to 
            //get the Rdistorted
          mFisheyeCorrect = new ArrayList(mFELimit);
            //i corresponds to Rundist/focalLengthInPixels * 1000 (to get integers)
          for (int i = 0; i < mFELimit; i++)
          {
              double result = Math.Sqrt(1 - 1 / Math.Sqrt(1.0 + (double)i * i / 1000000.0)) * 1.4142136;
              mFisheyeCorrect.Add(result);
          }
      }

      public Bitmap RemoveFisheye(ref Bitmap aImage, double aFocalLinPixels)
      {
          Bitmap correctedImage = new Bitmap(aImage.Width, aImage.Height);
             //The center points of the image
          double xc = aImage.Width / 2.0;
          double yc = aImage.Height / 2.0;
          Boolean xpos, ypos;
            //Move through the pixels in the corrected image; 
            //set to corresponding pixels in distorted image
          for (int i = 0; i < correctedImage.Width; i++)
          {
              for (int j = 0; j < correctedImage.Height; j++)
              {
                     //which quadrant are we in?
                  xpos = i > xc;
                  ypos = j > yc;
                     //Find the distance from the center
                  double xdif = i-xc;
                  double ydif = j-yc;
                     //The distance squared
                  double Rusquare = xdif * xdif + ydif * ydif;
                     //the angle from the center
                  double theta = Math.Atan2(ydif, xdif);
                     //find index for lookup table
                  int index = (int)(Math.Sqrt(Rusquare) / aFocalLinPixels * 1000);
                  if (index >= mFELimit) index = mFELimit - 1;
                     //calculated Rdistorted
                  double Rd = aFocalLinPixels * (double)mFisheyeCorrect[index]
                                        /mScaleFESize;
                     //calculate x and y distances
                  double xdelta = Math.Abs(Rd*Math.Cos(theta));
                  double ydelta = Math.Abs(Rd * Math.Sin(theta));
                     //convert to pixel coordinates
                  int xd = (int)(xc + (xpos ? xdelta : -xdelta));
                  int yd = (int)(yc + (ypos ? ydelta : -ydelta));
                  xd = Math.Max(0, Math.Min(xd, aImage.Width-1));
                  yd = Math.Max(0, Math.Min(yd, aImage.Height-1));
                     //set the corrected pixel value from the distorted image
                  correctedImage.SetPixel(i, j, aImage.GetPixel(xd, yd));
              }
          }
          return correctedImage;
      }
}

Ich habe diese PDF -Datei gefunden und habe bewiesen, dass die Mathematik korrekt sind (mit Ausnahme der Zeile vd = *xd**fv+v0 which should say vd = **yd**+fv+v0).

http://perception.inrialpes.fr/cava_dataset/site/files/calibration_opencv.pdf

Es wird nicht all die neuesten Koeffektive verwendet, die OpenCV zur Verfügung haben, aber ich bin sicher, dass es ziemlich leicht angepasst werden könnte.

double k1 = cameraIntrinsic.distortion[0];
double k2 = cameraIntrinsic.distortion[1];
double p1 = cameraIntrinsic.distortion[2];
double p2 = cameraIntrinsic.distortion[3];
double k3 = cameraIntrinsic.distortion[4];
double fu = cameraIntrinsic.focalLength[0];
double fv = cameraIntrinsic.focalLength[1];
double u0 = cameraIntrinsic.principalPoint[0];
double v0 = cameraIntrinsic.principalPoint[1];
double u, v;


u = thisPoint->x; // the undistorted point
v = thisPoint->y;
double x = ( u - u0 )/fu;
double y = ( v - v0 )/fv;

double r2 = (x*x) + (y*y);
double r4 = r2*r2;

double cDist = 1 + (k1*r2) + (k2*r4);
double xr = x*cDist;
double yr = y*cDist;

double a1 = 2*x*y;
double a2 = r2 + (2*(x*x));
double a3 = r2 + (2*(y*y));

double dx = (a1*p1) + (a2*p2);
double dy = (a3*p1) + (a1*p2);

double xd = xr + dx;
double yd = yr + dy;

double ud = (xd*fu) + u0;
double vd = (yd*fv) + v0;

thisPoint->x = ud; // the distorted point
thisPoint->y = vd;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top