53

賞金ステータスの更新:

線形レンズを座標からdestination座標にマッピングする方法を発見しましたsource

魚眼レンズから直線に移動する中心からの半径距離をどのように計算しますか?

  • 1)。私は実際にそれを逆にし、ソース座標を宛先座標にマッピングするのに苦労しています。私が投稿した変換関数のスタイルのコードで、逆は何ですか?

  • 2)。また、一部のレンズでは歪みの補正が不完全であることもわかります。おそらく、厳密に線形ではないレンズです。これらのレンズのソースとデスティネーションの座標を往復するのに相当するものは何ですか? 繰り返しますが、数式だけでなく、より多くのコードをお願いします...


最初に述べた質問:

魚眼レンズで撮影した写真の位置を説明するポイントがいくつかあります。

これらの点を直線座標に変換したい。画像の歪みを解消したい。

魚眼効果を生成する方法のこの説明を見つけましたが、それを元に戻す方法は見つかりませんでした。

ツールを使用してそれを行う方法を説明するブログ投稿もあります。これらの写真はそれからのものです:

(1) :SOURCE 元の写真のリンク

入力 :修正する魚眼歪みのある元の画像。

(2) :DESTINATION 元の写真のリンク

出力 :修正された画像 (技術的には遠近法も含みますが、それは別の手順です)。

魚眼レンズから直線に移動する中心からの半径距離をどのように計算しますか?

私の関数スタブは次のようになります。

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;
}

または、ポイントを見つける前に画像を魚眼レンズから直線に変換することもできますが、OpenCV のドキュメントに完全に困惑しています。OpenCV でそれを行う簡単な方法はありますか?また、ライブ ビデオ フィードに対して実行するのに十分なパフォーマンスを発揮しますか?

4

7 に答える 7

34

あなたが言及した説明は、ピンホールカメラ(レンズの歪みを導入しないカメラ)による投影は、によってモデル化されていると述べています

R_u = f*tan(theta)

一般的な魚眼レンズカメラによる投影(つまり、歪んだ)は、次のようにモデル化されます。

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

R_dとシータはすでに知っています。カメラの焦点距離(fで表される)を知っている場合、画像を修正すると、R_dとシータの観点からR_uを計算することになります。言い換えると、

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

あなたが探している式です。焦点距離fの推定は、カメラをキャリブレーションするか、画像がどの程度補正されているかについてユーザーにフィードバックを提供させるか、元のシーンの知識を使用するなどの他の手段によって解決できます。

OpenCVを使用して同じ問題を解決するには、カメラの固有のパラメーターとレンズの歪み係数を取得する必要があります。たとえば、OpenCVの学習の第11章を参照してください(修正を確認することを忘れないでください)。次に、レンズの歪みを元に戻すために、このようなプログラム(OpenCV用のPythonバインディングで記​​述された)を使用できます。

#!/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)

また、OpenCVは、リンクしたWebページのレンズ歪みモデルとは非常に異なるレンズ歪みモデルを使用していることに注意してください。

于 2010-03-21T14:13:38.010 に答える
8

(オリジナルのポスター、代替品を提供)

次の関数は、宛先(直線)座標をソース(魚眼レンズで歪んだ)座標にマップします。 (私はそれを元に戻すのに助けていただければ幸いです)

試行錯誤しながらこの点に到達しました。このコードが機能している理由を根本的に理解しておらず、説明と精度の向上に感謝しています。

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)))

3.0の係数で使用すると、例として使用されている画像の歪みを正常に解消できます(品質補間は試みませんでした)。

デッドリンク

(そして、これは比較のためにブログ投稿からのものです:)

Panotoolsの使用

于 2010-03-23T17:44:24.123 に答える
4

数式が正確であると思われる場合は、次のように trig を使用して正確な数式を計算できます。

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

ただし、@jmbr が言うように、実際のカメラの歪みはレンズとズームに依存します。固定式に頼るのではなく、多項式展開を試してみることをお勧めします。

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

最初に A を微調整し、次に高次の係数を微調整することで、適切なローカル関数を計算できます (展開の形式は問題の対称性を利用します)。特に、上記の理論関数を近似する初期係数を計算できるはずです。

また、良い結果を得るには、補間フィルターを使用して補正済みの画像を生成する必要があります。歪みが大きすぎない限り、画像を線形に再スケーリングするために使用する種類のフィルターを問題なく使用できます。

編集:あなたの要求に従って、上記の式の同等の倍率:

(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)

上記の式を tan(Rin/f) と並べてプロットすると、形状が非常に似ていることがわかります。基本的に、sin(w) が w と大きく異なる前に、接線からの歪みが激しくなります。

逆式は次のようになります。

Rin/f = [Rout/f] / sqrt( sqrt(([Rout/f]^2+1) * (sqrt([Rout/f]^2+1) + 1) / 2 )
于 2010-03-23T08:11:56.337 に答える
3

hereの数式をやみくもに実装したので、必要なことを行うことは保証できません。

auto_zoomパラメータの値を取得するために使用しzoomます。


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
于 2010-03-24T14:28:31.907 に答える
3

私はJMBRがしたことを基本的に逆にしました。彼は、歪んだ画像の半径 (Rd、つまり、画像の中心からのピクセル単位の距離) を取得し、歪んでいない画像の半径である Ru の式を見つけました。

あなたは反対に行きたいです。歪みのない (処理された画像) の各ピクセルについて、歪みのある画像の対応するピクセルが何であるかを知りたいとします。つまり、与えられた (xu, yu) --> (xd, yd) です。次に、歪みのないイメージの各ピクセルを、歪みのあるイメージの対応するピクセルに置き換えます。

JMBR が行ったところから始めて、逆を行い、Ru の関数として Rd を見つけます。私は得る:

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

ここで、f はピクセル単位の焦点距離です (後で説明します) r = Ru/f

私のカメラの焦点距離は2.5mmでした。私の CCD の各ピクセルのサイズは 6 um 四方でした。したがって、f は 2500/6 = 417 ピクセルでした。これは、試行錯誤によって見つけることができます。

Rd を見つけると、極座標を使用して歪んだ画像内の対応するピクセルを見つけることができます。

中心点からの各ピクセルの角度は同じです。

theta = arctan( (yu-yc)/(xu-xc) ) ここで、xc、yc は中心点です。

それで、

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

自分がどの象限にいるのかを確認してください。

これが私が使用したC#コードです

 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;
      }
}
于 2013-06-06T23:41:20.400 に答える
2

このpdfファイルを見つけて、数学が正しいことを証明しました(行を除くvd = *xd**fv+v0 which should say vd = **yd**+fv+v0)。

http://perception.inrialpes.fr/CAVA_Dataset/Site/files/Calibration_OpenCV.pdf

OpenCV が利用できる最新の係数をすべて使用しているわけではありませんが、かなり簡単に適応できると確信しています。

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;
于 2011-08-16T10:05:49.773 に答える