6

魚眼レンズで撮った大量の写真があります。写真に画像処理(エッジ検出など)を行いたいので、結果に大きな影響を与えるたる型の歪みを除去したいと考えています。

いくつかの調査と多くの記事を読んだ後、このページを見つけました:この問題を解決するためのアルゴリズム(およびいくつかの式)について説明しています。

M = a *rcorr^3 + b * rcorr^2 + c * rcorr + d
rsrc = (a * rcorr^3 + b * rcorr^2 + c * rcorr + d) * rcorr

rsrc = ソース画像の中心からの
ピクセルの距離 rcorr = 補正された画像の中心からのピクセルの距離
a,b,c = 画像の歪み d = 画像の線形スケーリング

これらの式を使用して、これを Java アプリケーションに実装しようとしました。残念ながら、それは機能せず、機能させることができませんでした。「修正された」画像は、元の写真とはまったく異なり、中央にいくつかの不思議な円が表示されます。ここを見て:

http://imageshack.us/f/844/barreldistortioncorrect.jpg/ (これは以前、青い壁の前にいる白い牛の写真でした)

これが私のコードです:

protected int[] correction(int[] pixels) {

    //
    int[] pixelsCopy = pixels.clone();

    // parameters for correction
    double paramA = 0.0; // affects only the outermost pixels of the image
    double paramB = -0.02; // most cases only require b optimization
    double paramC = 0.0; // most uniform correction
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image

    //
    for(int x = 0; x < dstView.getImgWidth(); x++) {
        for(int y = 0; y < dstView.getImgHeight(); y++) {

            int dstX = x;
            int dstY = y;

            // center of dst image
            double centerX = (dstView.getImgWidth() - 1) / 2.0;
            double centerY = (dstView.getImgHeight() - 1) / 2.0;

            // difference between center and point
            double diffX = centerX - dstX;
            double diffY = centerY - dstY;
            // distance or radius of dst image
            double dstR = Math.sqrt(diffX * diffX + diffY * diffY);

            // distance or radius of src image (with formula)
            double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR;

            // comparing old and new distance to get factor
            double factor = Math.abs(dstR / srcR);
            // coordinates in source image
            double srcXd = centerX + (diffX * factor);
            double srcYd = centerY + (diffX * factor);

            // no interpolation yet (just nearest point)
            int srcX = (int)srcXd;
            int srcY = (int)srcYd;

            if(srcX >= 0 && srcY >= 0 && srcX < dstView.getImgWidth() && srcY < dstView.getImgHeight()) {

                int dstPos = dstY * dstView.getImgWidth() + dstX;
                pixels[dstPos] = pixelsCopy[srcY * dstView.getImgWidth() + srcX];
            }
        }
    }

    return pixels;
}

私の質問は次のとおりです
。1) この式は正しいですか?
2) その数式をソフトウェアに変換するのを間違えたのでしょうか?
3) 他のアルゴリズムがあります (例: openCV で魚眼レンズ効果をシミュレートするには?または wiki/Distortion_(optics))。

ご協力いただきありがとうございます!

4

4 に答える 4

10

あなたが持っている主なバグは、アルゴリズムが r_corr と r_src が min((xDim-1)/2, (yDim-1)/2) の単位であることを指定していることです。これは、パラメータ値がソース画像のサイズに依存しないように計算を正規化するために行う必要があります。コードをそのまま使用すると、paramB にはるかに小さい値を使用する必要があります。たとえば、paramB = 0.00000002 (サイズが 2272 x 1704 の画像の場合) でうまくいきました。

また、結果の画像が元の画像と比較して 180 度回転する原因となる中心からの差の計算にもバグがあります。

これらの両方のバグを修正すると、次のようになります。

protected static int[] correction2(int[] pixels, int width, int height) {
    int[] pixelsCopy = pixels.clone();

    // parameters for correction
    double paramA = -0.007715; // affects only the outermost pixels of the image
    double paramB = 0.026731; // most cases only require b optimization
    double paramC = 0.0; // most uniform correction
    double paramD = 1.0 - paramA - paramB - paramC; // describes the linear scaling of the image

    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            int d = Math.min(width, height) / 2;    // radius of the circle

            // center of dst image
            double centerX = (width - 1) / 2.0;
            double centerY = (height - 1) / 2.0;

            // cartesian coordinates of the destination point (relative to the centre of the image)
            double deltaX = (x - centerX) / d;
            double deltaY = (y - centerY) / d;

            // distance or radius of dst image
            double dstR = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

            // distance or radius of src image (with formula)
            double srcR = (paramA * dstR * dstR * dstR + paramB * dstR * dstR + paramC * dstR + paramD) * dstR;

            // comparing old and new distance to get factor
            double factor = Math.abs(dstR / srcR);

            // coordinates in source image
            double srcXd = centerX + (deltaX * factor * d);
            double srcYd = centerY + (deltaY * factor * d);

            // no interpolation yet (just nearest point)
            int srcX = (int) srcXd;
            int srcY = (int) srcYd;

            if (srcX >= 0 && srcY >= 0 && srcX < width && srcY < height) {
                int dstPos = y * width + x;
                pixels[dstPos] = pixelsCopy[srcY * width + srcX];
            }
        }
    }

    return pixels;
}

このバージョンでは、LensFun などの既存のレンズ データベースのパラメーター値を使用できます (ただし、各パラメーターの符号を反転する必要があります)。アルゴリズムを説明するページは、http://mipav.cit.nih.gov/pubwiki/index.php/Barrel_Distortion_Correctionにあります。

于 2013-05-04T07:07:23.197 に答える
2

あなたの円はこの行によって引き起こされていると思います:

double srcYd = centerY + (diffX * factor);

私が推測しているのは、次のとおりです。

double srcYd = centerY + (diffY * factor);
于 2013-01-03T23:37:26.050 に答える
0

あなたの価値観は非常に極端なので、極端な結果が表示されます。

a=0、b=0、c=1 を試してください。あなたのプログラムが正しければ、元の画像が表示されるはずです。次に、c と b を徐々に変更します。0.1 ずつ変更することから始めるのがよいでしょう。

于 2012-12-20T08:46:45.797 に答える