画像処理の高速化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とかが当たり前なんで、遅いというのは
無視できないことなのです。
んじゃ画像処理にテンプレートは向かないのかというと、全くそういうことではない。要は使いどころですね。
(この話はまた別の機会に)
※ソースコード色付けとかしたいけどやりかたわかんねー><