5

以下のソース コードなど、固定サイズのボクセル データの勾配を計算する最も効率的な方法は何ですか。空間の任意の点でグラデーションが必要であることに注意してください。グラデーションは、マーチング キューブの実装で法線を推定するために使用されます。

#import <array>

struct VoxelData {
    VoxelData(float* data, unsigned int xDim, unsigned int yDim, unsigned int zDim)
    :data(data), xDim(xDim), yDim(yDim), zDim(zDim)
    {}

    std::array<float,3> get_gradient(float x, float y, float z){
        std::array<float,3> res;
        // compute gradient efficiently
        return res;
    }

    float get_density(int x, int y, int z){
        if (x<0 || y<0 || z<0 || x >= xDim || y >= yDim || z >= zDim){
            return 0;
        }
        return data[get_element_index(x, y, z)];
    }

    int get_element_index(int x, int y, int z){
        return x * zDim * yDim + y*zDim + z;
    }

    const float* const data;

    const unsigned int xDim;
    const unsigned int yDim;
    const unsigned int zDim;

};

更新 1 問題のデモ プロジェクトは次の場所にあります。

https://github.com/mortennobel/OpenGLVoxelizer

現在、出力は下の図のようになっています (MooseBoys コードに基づく):

更新 2 私が探しているソリューションは、視覚化で法線として使用され、以下のような視覚的なアーティファクトを避ける必要があるため、かなり正確なグラデーションを提供する必要があります。

ここに画像の説明を入力

ユーザー例からの更新2ソリューションは次のとおりです。

ここに画像の説明を入力

4

4 に答える 4

2

多くの実装における最適化の重要な手法の 1 つに、時間と空間のトレードオフがあります。提案として、結果を事前に計算してキャッシュできる場所ならどこでも検討する価値があるかもしれません。

于 2014-01-30T14:37:15.837 に答える
2

一般に、ソーベル フィルターは、単純な中心傾向よりもわずかに優れた結果を提供しますが、計算に時間がかかります (ソーベルは本質的に、中心傾向と組み合わされた滑らかなフィルターです)。従来のソーベルでは 26 サンプルの重み付けが必要ですが、中心傾向では 6 サンプルしか必要ありません。ただし、トリックがあります。GPU を使用すると、ハードウェアベースのトライリニア補間を無料で取得できます。これは、8 つのテクスチャ読み取りでソーベルを計算できることを意味し、これは GPU 全体で並列に実行できます。次のページは、GLSL http://www.mccauslandcenter.sc.edu/mricrogl/notes/gradientsを使用したこの手法を示してい ます。あなたのプロジェクトでは、おそらく GPU で勾配を計算し、GPGPU メソッドを使用して結果をさらに処理するために GPU から CPU へ。

于 2014-08-28T14:52:23.780 に答える
1

MooseBoys は、コンポーネントごとの線形補間を既に投稿しています。(int)xただし、ある値から次の値に変化するところはどこでも、y および z コンポーネントでは不連続です(他のコンポーネントについても同じです)。これは、あなたが見ているように大雑把な絵になるかもしれません。余裕のあるパフォーマンスがある場合は、それだけでなく、同様に検討することでこれを改善でき(int)xます(int)(x+1)。これは次のようになります

std::array<float,3> get_gradient(float x, float y, float z){
    std::array<float,3> res;

    int xim = (int)(x + 0.5f);
    float xfm = x + 0.5f - xi;
    int yim = (int)(y + 0.5f);
    float yfm = y + 0.5f - yi;
    int zim = (int)(z + 0.5f);
    float zfm = z + 0.5f - zi;
    int xi = (int)x;
    float xf = x - xi;
    int yi = (int)y;
    float yf = y - yi;
    int zi = (int)z;
    float zf = z - zi;


    float xd0 = yf*(          zf *get_density(xim - 1, yi+1, zi+1) 
                    + (1.0f - zf)*get_density(xim - 1, yi+1, zi))
                +(1.0f - yf)*(zf *get_density(xim - 1, yi  , zi+1) 
                    + (1.0f - zf)*get_density(xim - 1, yi  , zi));
    float xd1 = yf*(          zf *get_density(xim,     yi+1, zi+1) 
                    + (1.0f - zf)*get_density(xim,     yi+1, zi))
                +(1.0f - yf)*(zf *get_density(xim,     yi  , zi+1) 
                    + (1.0f - zf)*get_density(xim,     yi  , zi));
    float xd2 = yf*(          zf *get_density(xim + 1, yi+1, zi+1) 
                    + (1.0f - zf)*get_density(xim + 1, yi+1, zi))
                +(1.0f - yf)*(zf *get_density(xim + 1, yi  , zi+1) 
                    + (1.0f - zf)*get_density(xim + 1, yi  , zi));
    res[0] = (xd1 - xd0) * (1.0f - xfm) + (xd2 - xd1) * xfm;

    float yd0 = xf*(          zf *get_density(xi+1, yim-1, zi+1) 
                    + (1.0f - zf)*get_density(xi+1, yim-1, zi))
                +(1.0f - xf)*(zf *get_density(xi  , yim-1, zi+1) 
                    + (1.0f - zf)*get_density(xi  , yim-1, zi));
    float yd1 = xf*(          zf *get_density(xi+1, yim  , zi+1) 
                    + (1.0f - zf)*get_density(xi+1, yim  , zi))
                +(1.0f - xf)*(zf *get_density(xi  , yim  , zi+1) 
                    + (1.0f - zf)*get_density(xi  , yim  , zi));
    float yd2 = xf*(          zf *get_density(xi+1, yim+1, zi+1) 
                    + (1.0f - zf)*get_density(xi+1, yim+1, zi))
                +(1.0f - xf)*(zf *get_density(xi  , yim+1, zi+1) 
                    + (1.0f - zf)*get_density(xi  , yim+1, zi));
    res[1] = (yd1 - yd0) * (1.0f - yfm) + (yd2 - yd1) * yfm;

    float zd0 = xf*(          yf *get_density(xi+1, yi+1, zim-1) 
                    + (1.0f - yf)*get_density(xi+1, yi  , zim-1))
                +(1.0f - xf)*(yf *get_density(xi,   yi+1, zim-1) 
                    + (1.0f - yf)*get_density(xi,   yi  , zim-1));
    float zd1 = xf*(          yf *get_density(xi+1, yi+1, zim) 
                    + (1.0f - yf)*get_density(xi+1, yi  , zim))
                +(1.0f - xf)*(yf *get_density(xi,   yi+1, zim) 
                    + (1.0f - yf)*get_density(xi,   yi  , zim));
    float zd2 = xf*(          yf *get_density(xi+1, yi+1, zim+1) 
                    + (1.0f - yf)*get_density(xi+1, yi  , zim+1))
                +(1.0f - xf)*(yf *get_density(xi,   yi+1, zim+1) 
                    + (1.0f - yf)*get_density(xi,   yi  , zim+1));
    res[2] = (zd1 - zd0) * (1.0f - zfm) + (zd2 - zd1) * zfm;
    return res;
}

これはおそらくもう少し簡潔に書くことができますが、おそらくこの方法でも何が起こっているかを見ることができます. それでも十分に滑らかでない場合は、キュービック/スプライン補間などを検討する必要があります。

于 2014-01-28T18:47:55.957 に答える