2016-12-20 11 views
1

私はイメージを持っています。すべてのピクセルにRGB強度に関する情報が含まれています。今私は、これらのチャンネルの強度を合計したいと思いますが、私はまたどのチャンネルの強度を合計するかを選択したいと思います。私は私ができる場合は、すべての条件を破棄したい画像における画素毎に、この関数を呼び出しますのでコンパイル時に関数を生成

int intensity(const unsiged char* pixel, bool red, bool green, bool blue){ 
    return 0 + (red ? pixel[0] : 0) + (green ? pixel[1] : 0) + (blue ? pixel[2] : 0); 
} 

:このStraightforwad実装は次のようになります。今、私は任意の条件なしで画像ループや使用機能に入る前に、この発電機を使用することができます

std::function<int(const unsigned char* pixel)> generateIntensityAccumulator(
    const bool& accumulateRChannel, 
    const bool& accumulateGChannel, 
    const bool& accumulateBChannel) 
    { 
    if (accumulateRChannel && accumulateGChannel && accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[0]) + static_cast<int>(pixel[1]) + static_cast<int>(pixel[2]); 
      }; 
     } 

     if (!accumulateRChannel && accumulateGChannel && accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[1]) + static_cast<int>(pixel[2]); 
      }; 
     } 

     if (!accumulateRChannel && !accumulateGChannel && accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[2]); 
      }; 
     } 

     if (!accumulateRChannel && !accumulateGChannel && !accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return 0; 
      }; 
     } 

     if (accumulateRChannel && !accumulateGChannel && !accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[0]); 
      }; 
     } 

     if (!accumulateRChannel && accumulateGChannel && !accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[1]); 
      }; 
     } 

     if (accumulateRChannel && !accumulateGChannel && accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[0]) + static_cast<int>(pixel[2]); 
      }; 
     } 

     if (accumulateRChannel && accumulateGChannel && !accumulateBChannel){ 
      return [](const unsigned char* pixel){ 
       return static_cast<int>(pixel[0]) + static_cast<int>(pixel[1]); 
      }; 
     } 
    } 

:だから私は、私はすべてのケースのための機能を持っている必要が推測

... 

auto accumulator = generateIntensityAccumulator(true, false, true); 

for(auto pixel : pixels){ 
auto intensity = accumulator(pixel); 
} 

... 

しかし、それは多くのですこのような単純な作業のための書き込みをすることができます。私は、これを実現するためのより良い方法があると感じています。たとえば、コンパイラを使って私にとって汚い作業を行い、上記のすべてのケースを生成します。誰かが私を正しい方向に向けることができますか?

+1

上記のパフォーマンスを実際にテストしましたか?プロセッサーは一般的に "前回と同じ結果"を仮定してブランチを最適化するので、ループ外で単純なブールテストを動かすことは大変重要であると私は驚いています... –

+0

私は認めていません - パフォーマンスが向上します。私は支店の予感(http://igoro.com/archive/fast-and-slow-if-statements-branch-prediction-in-modern-processors/)について読んで、私のケースではうまくいくと思います。ありがとう! – Amadeusz

+0

私は間違っている可能性があります...私はちょうど複雑すぎることをする前にパフォーマンステストを実行することを検討したいと思います。 –

答えて

2

std::functionをこのように使用すると、コンパイラが可能な限りインライン展開して最適化する機会を与えられないため、このようなことがあります。

あなたがしようとしているのは、テンプレートのための良い仕事です。整数を使うので、式自体は最適化され、各バージョンの特化を書く必要はありません。この例を見てください:

-O2クラン3.9でコンパイル
#include <array> 
#include <chrono> 
#include <iostream> 
#include <random> 
#include <vector> 

template <bool AccumulateR, bool AccumulateG, bool AccumulateB> 
inline int accumulate(const unsigned char *pixel) { 
    static constexpr int enableR = static_cast<int>(AccumulateR); 
    static constexpr int enableG = static_cast<int>(AccumulateG); 
    static constexpr int enableB = static_cast<int>(AccumulateB); 
    return enableR * static_cast<int>(pixel[0]) + 
     enableG * static_cast<int>(pixel[1]) + 
     enableB * static_cast<int>(pixel[2]); 
} 

int main(void) { 
    std::vector<std::array<unsigned char, 3>> pixels(
     1e7, std::array<unsigned char, 3>{0, 0, 0}); 

    // Fill up with randomness 
    std::random_device rd; 
    std::uniform_int_distribution<unsigned char> dist(0, 255); 
    for (auto &pixel : pixels) { 
    pixel[0] = dist(rd); 
    pixel[1] = dist(rd); 
    pixel[2] = dist(rd); 
    } 

    // Measure perf 
    using namespace std::chrono; 

    auto t1 = high_resolution_clock::now(); 
    int sum1 = 0; 
    for (auto const &pixel : pixels) 
    sum1 += accumulate<true, true, true>(pixel.data()); 
    auto t2 = high_resolution_clock::now(); 
    int sum2 = 0; 
    for (auto const &pixel : pixels) 
    sum2 += accumulate<false, true, false>(pixel.data()); 
    auto t3 = high_resolution_clock::now(); 

    std::cout << "Sum 1 " << sum1 << " in " 
      << duration_cast<milliseconds>(t2 - t1).count() << "ms\n"; 
    std::cout << "Sum 2 " << sum2 << " in " 
      << duration_cast<milliseconds>(t3 - t2).count() << "ms\n"; 
} 

、私のCPUの利回りこの結果を:

Sum 1 -470682949 in 7ms 
Sum 2 1275037960 in 2ms 

あなたがする必要があるかもしれません、私たちはここにオーバーフローを持っているという事実に気づいてください。 intより大きなものを使用してください。 uint64_tの場合があります。アセンブリコードを調べると、関数の2つのバージョンがインライン化され、最適化されていることがわかります。

1

最初のものが最初です。 pixelを受け取るstd::functionを書き込まないでください。 pixel(ピクセルの走査線)の連続した範囲を取るものを書きます。えっ、

template<bool red, bool green, bool blue> 
int intensity(const unsiged char* pixel){ 
    return (red ? pixel[0] : 0) + (green ? pixel[1] : 0) + (blue ? pixel[2] : 0); 
} 

かなりシンプル:

第二に、あなたはintensitytemplateバージョンを書きたいですか?それはあなたの手作りのバージョンに最適化されます。

template<std::size_t index> 
int intensity(const unsiged char* pixel){ 
    return intensity< index&1, index&2, index&4 >(pixel); 
} 

これはintensity<bool, bool, bool>の呼び出すためindexのビットからマッピングします。今すぐスキャンラインバージョンのため:

int(*)(const unsigned char* pel, std::size_t pixels) 
scanline_intensity(bool red, bool green, bool blue) { 
    static const auto table[] = { 
    sum_intensity<0b000>, sum_intensity<0b001>, 
       sum_intensity<0b010>, sum_intensity<0b011>, 
    sum_intensity<0b100>, sum_intensity<0b101>, 
       sum_intensity<0b110>, sum_intensity<0b111>, 
    }; 
    std::size_t index = red + green*2 + blue*4; 
    return sum_intensity[index]; 
} 

およびdone:

template<std::size_t index, std::size_t pixel_stride=3> 
int sum_intensity(const unsiged char* pixel, std::size_t count){ 
    int value = 0; 
    while(count--) { 
    value += intensity<index>(pixel); 
    pixel += pixel_stride; 
    } 
    return value; 
} 

現在、当社の走査線強度計算を生成することができます。

これらの手法は一般的にすることができますが、一般的な手法は必要ありません。

ピクセルストライドが3ではない場合(アルファチャンネルがあるとします)、sum_intensityを渡す必要があります(テンプレートパラメータとして理想的です)。

関連する問題