画像処理の高速化1

何回かに分けて画像処理の高速化について書こうと思います。

画像処理の基本は、ピクセル走査。
素直に書くと、

int channel;
int width;
int height;
unsigned char* data = new unsigned char[width * height * channel];

for ( int y = 0; y < height; y++ )
{
for ( int x = 0; x < width; x++ )
{
for ( int c = 0; c < channel; c++ )
{
data[y * width * channel + x * channel + c] = F(data[y * width * channel + x * channel + c]);
}
}
}

ってなると思う。
ただ、この書き方はパディングをまったく考慮していません。
パディングっていうのは、画像サイズの幅を4バイト境界にアラインするためのゴミデータのこと。

ここで、簡単に高速化できそうなポイントとして、
1.for文のネストを減らす。
2.配列アクセスじゃなくてポインタアクセスにする。
がすぐに思いつく。

まず、1。
for ( int i = 0; i < width * height * channel; i++ )
{
data[i] = F(data[i]);
}

すっごいシンプル。
けど、この書き方は空間フィルタ(周辺画素も計算に必要)だと逆に見にくくなる。
なので、外側の2重ループは残すことが多い。

次に、2。
for ( int y = 0; y < height; y++ )
{
for ( int x = 0; x < width; x++, data += channel )
{
for ( int c = 0; c < channel; c++ )
{
*(data + c) = F(*(data + c));
}
}
}

これは意外にそこまで速くならない。
配列アクセスでも連続した配列データなら、シーケンシャルアクセスとして
最適化してくれるみたい。

けど、最適化してくれないパターンがこれ。
boost::gil::bgra8_view_t data;

for ( int y = 0; y < height; y++ )
{
for ( int x = 0; x < width; x++ )
{
for ( int c = 0; c < channel; c++ )
{
data(x, y)[c] = F(data(x, y)[c]);
}
}
}

Boost.GIL。
データアクセスにテンプレート使っちゃってるもんだから、
シーケンシャルに最適化されないのです。

Boost.GILは、データの抽象化に特化しすぎて、速度が完全に犠牲になっちゃってる残念さがある。

画像処理にそこまで速度を求めない場合や、画像データのサイズが小さい場合は気にならないのかもしれないけど、
僕が普段扱う画像データが、数十MBとかが当たり前なんで、遅いというのは
無視できないことなのです。

んじゃ画像処理にテンプレートは向かないのかというと、全くそういうことではない。要は使いどころですね。
(この話はまた別の機会に)

ソースコード色付けとかしたいけどやりかたわかんねー><