blocked_range2dで画像を作ってみた
blocked_range2dという2Dのサンプリングにうってつけのものがあるので使い方を調べてみた。
Tutorialによるとシングルコアでもリニアに走査するよりメモリのキャッシュヒット率が上がるのでよいものだというニュアンスのことが書いてあったがそうなのか。
#include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <fstream> #include "tbb/task_scheduler_init.h" #include "tbb/blocked_range2d.h" #include "tbb/parallel_for.h" struct Circle { float center_x; float center_y; float r; unsigned char intersect(const int x, const int y)const { float dx=x-center_x; float dy=y-center_y; float d=dx*dx+dy*dy; float sqr=r*r; if(d>sqr){ return 0; } return std::sqrt(sqr-d)/r * 255.0f; } }; struct Image { const int w; const int h; std::vector<unsigned char> rgba32; Image(const int width, const int height) : w(width), h(height), rgba32(width*height*4) {} void set(const int x, const int y, const unsigned char r, const unsigned char g, const unsigned char b, const unsigned char a) { unsigned char *p=&rgba32[(y*w+x)*4]; p[0]=r; p[1]=g; p[2]=b; p[3]=a; } void DWORD(std::ostream &os, unsigned long rhs) { unsigned char buf[4]; buf[0]=rhs & 0xFF; buf[1]=(rhs>>8) & 0xFF; buf[2]=(rhs>>16) & 0xFF; buf[3]=(rhs>>24) & 0xFF; os.write((char*)buf, 4); } void WORD(std::ostream &os, unsigned short rhs) { unsigned char buf[2]; buf[0]=rhs & 0xFF; buf[1]=(rhs>>8) & 0xFF; os.write((char*)buf, 2); } void LONG(std::ostream &os, long rhs) { unsigned char buf[4]; buf[0]=rhs & 0xFF; buf[1]=(rhs>>8) & 0xFF; buf[2]=(rhs>>16) & 0xFF; buf[3]=(rhs>>24) & 0xFF; os.write((char*)buf, 4); } void write(const char *path) { std::ofstream io(path, std::ios::binary); if(!path){ return; } // BITMAPFILEHEADER io << 'B'; io << 'M'; DWORD(io, 54+rgba32.size()); WORD(io, 0); WORD(io, 0); DWORD(io, 54); // BITMAPINFOHEADER DWORD(io, 40); LONG(io, w); LONG(io, h); WORD(io, 1); WORD(io, 32); DWORD(io, 0); DWORD(io, w*h*4); LONG(io, 0); LONG(io, 0); DWORD(io, 0); DWORD(io, 0); // rawdata io.write((const char *)&rgba32[0], rgba32.size()); } }; // blocked_range2d struct RectFunctor { Image ℑ const Circle &circle; RectFunctor(Image &_image, const Circle &_circle) : image(_image), circle(_circle) {} void operator()(const tbb::blocked_range2d<size_t>& r)const { /* 修正 tbb::blocked_range2d<size_t>::row_range_type vertical =r.rows(); tbb::blocked_range2d<size_t>::row_range_type horizontal =r.cols(); */ tbb::blocked_range2d<size_t>::row_range_type horizontal =range.rows(); tbb::blocked_range2d<size_t>::col_range_type vertical =range.cols(); for(size_t y=vertical.begin(); y!=vertical.end(); ++y){ for(size_t x=horizontal.begin(); x!=horizontal.end(); ++x){ if(x==horizontal.begin() || x==horizontal.end() || y==vertical.begin() || y==vertical.end()){ image.set(x, y, 0, 0, 255, 0); } else{ unsigned char gray_scale=circle.intersect(x, y); image.set(x, y, gray_scale, gray_scale, gray_scale, 0); } } } } }; void write_circle_2D(Image &image, const Circle &circle) { tbb::parallel_for(tbb::blocked_range2d<size_t>(0, image.w, 0, image.h), RectFunctor(image, circle) ,tbb::auto_partitioner() ); } int main(int argc, char **argv) { Image image(150, 150); std::fill(image.rgba32.begin(), image.rgba32.end(), 0); Circle circle; circle.center_x=image.w/2; circle.center_y=image.h/2; circle.r=std::min(image.w, image.h)/2-5; tbb::task_scheduler_init init; write_circle_2D(image, circle); image.write("tmp.bmp"); return 0; }
ファンクタ内のImageへのリファレンスをmutableにする必要があるかと
思ったが必要なかった。
計画ではparallel_reduceの方を使うつもりなのでよいのだけど。