在實作影像處理演算的過程或是實務的功能整合中, 都圍繞著 Halide::Runtime:: Buffer 的使用. 有時難以避免需要初始化預設值或是從其他 Buffer 物件複製資料. 這些可以透過 Buffer::fill() 或是 Buffer::copy_from 來做基本的填值處理.
對於一些相當 routine 但是卻又需要計算的部份, 通常會透過 loop 來走過每個座標, 甚至每個一個 value. 像是做簡單的 gamma correction 可能就會做這樣的動作:
Buffer<uint8_t, 2> src = Buffer<uint8_t>(w, h);
...
Buffer<uint8_t, 2> dst = Buffer<uint8_t>(w, h);
for(int y = 0; y < dst.height(); y++){
for(int x = 0; x < dst.width(); x++){
for(int c = 0; c < dst.channel(); c++){
dst(x, y, c) = gamma(src(x, y, c));
}
}
}
或是可能是格式轉換 (像是 RGB <-> YUV 或是這裡的 Demosaic):
Buffer<uint8_t, 2> src = Buffer<uint8_t>(w, h);
...
Buffer<uint8_t, 2> dst = Buffer<uint8_t>(w, h);
for(int y = 0; y < dst.height(); y++){
for(int x = 0; x < dst.width(); x++){
uint8_t r, g, b;
demosaic(src(x, y), r, g, b);
dst(x, y, 0) = r;
dst(x, y, 1) = g;
dst(x, y, 2) = b;
}
}
一旦數量很多, 或是 dimension 很大一一撰寫 loop 除了費時費力外, 也讓 code 不易維護與可讀性不佳. 對於這種問題, Halide 透過了 C++ lambda 的方式能夠簡短地處理, Halide Buffer 提供了兩個在其官方教學沒有提到的兩個實用的方式: for_each_value 與 for_each_element :
對於 Gamma 的例子可以簡短地改寫為:
Buffer<uint8_t, 2> dst = Buffer<uint8_t>(w, h);
dst.for_each_value([&](uint8_t &dval, uint8_t &sval){
dval = gamma(sval);
}, src);
而對於像 demosaic 的例子則可以改寫為:
Buffer<uint8_t, 2> dst = Buffer<uint8_t>(w, h);
dst.for_each_element([&](int x, int y){
uint8_t r, g, b;
demosaic(src(x, y), r, g, b);
dst(x, y, 0) = r;
dst(x, y, 1) = g;
dst(x, y, 2) = b;
});
這樣的作法也適合套用在一些特殊的初始化.
沒有留言:
張貼留言