现在的位置: 首页 > 综合 > 正文

Boost的动态多维数组-multi_array

2013年05月26日 ⁄ 综合 ⁄ 共 6792字 ⁄ 字号 评论关闭
Boost.MultiArray是一个动态多维数组库。它实现了一个通用、与标准库的容器一致的接口,并且具有与C++中内建的多维数组一样的接口和行为。

简短的例子

以下是使用 multi_array 的一个简短例子:

  1. #include "boost/multi_array.hpp"
  2. #include <cassert>
  3.  
  4. int main () {
  5.   // 创建一个 3 x 4 x 2 的 3D 数组
  6.   typedef boost::multi_array<double, 3> array_type;
  7.   typedef array_type::index index;
  8.   array_type myarray(boost::extents[3][4][2]);
  9.  
  10.   // 赋值到数组的元素
  11.   int values = 0;
  12.   for(index i = 0; i != 3; ++i)
  13.     for(index j = 0; j != 4; ++j)
  14.       for(index k = 0; k != 2; ++k)
  15.         myarray[i][j][k] = values++;
  16.  
  17.   // 校验元素值
  18.   int verify = 0;
  19.   for(index i = 0; i != 3; ++i)
  20.     for(index j = 0; j != 4; ++j)
  21.       for(index k = 0; k != 2; ++k)
  22.         assert(myarray[i][j][k] == verify++);
  23.  
  24.   return 0;
  25. }

上面的代码可以告诉我们这样一些信息:

  • boost::multi_array是一个模板类,第一个模板参数指定元素数据类型;第二个模板参数是一个数值,指出数组维度。
  • multi_array::index类型定义用于描述数据索引,一般来说它就是int
  • boost:: extents的连续多个[]操作用于指明每个维度的大小(另外,也可以使用boost::array来指明维度大小,如boost:: array<int,3> dims={3,4,2};
    array_type myarray(dims);)
  • boost::multi_array的读写接口和原生多维数组相同

把连续内存适配成多维数组

有时我们需要把已有的一段连续内存(比如一维数组)当作多维数组使用,Boost.MultiArray提供了multi_array_ref和const_multi_array_ref类用于把原始内存块适配成多维数组。

如,可以如下修改前例的代码,在这个代码里使用multi_array_ref代替multi_array,它把double buf[24]适配成了“double A[3][4][2]”:

  1. /* 原来的代码
  2.   typedef boost::multi_array<double, 3> array_type;
  3.   typedef array_type::index index;
  4.   array_type myarray(boost::extents[3][4][2]);
  5. */
  6.   typedef boost::multi_array_ref<double, 3> array_type;
  7.   typedef array_type::index index;
  8.   double buf[24]; //连续内存块
  9.   array_type myarray(buf, boost::extents[3][4][2]); //把buf适配成3 x 4 x 2 的3D数组
  10. ...
  11. Boost.MultiArray是一个动态多维数组库。它实现了一个通用、与标准库的容器一致的接口,并且具有与C++中内建的多维数组一样的接口和行为。

    子视图

    子 视图,在图像处理中定义为从一个大的图片(如100x100的图片)中取一小块(如左上角25,25到右下角75,75的50x50区域),把它看作一个 新的图片,这个新的图片即为子视图(可以参考本站GIL库教程)。多维数组的概念与此相似,作为聪明的你肯定已经知道是怎么一回事了。下面是生成子视图的 例子,还是以前例代码为基础,从3x4x2的myarray数组中产生一个2x2x2的子视图,其中第二维以2步进:

    1. ...
    2.   typedef array_type::index_range range;
    3.   array_type::array_view<3>::type myview =
    4.     myarray[ boost::indices[range(1,3)][range(0,4,2)][range(0,2)] ];
    5.  
    6.   for (array_type::index i = 0; i != 2; ++i)
    7.     for (array_type::index j = 0; j != 2; ++j)
    8.       for (array_type::index k = 0; k != 2; ++k)
    9.     assert(myview[i][j][k] == myarray[i+1][j*2][k]);

    上面的代码我们总结出的信息是:

    • multi_array的子视图类型由它的array_view::type类型定义确定,这个array_view是个模板类,其参数指定了视图的维度。
    • multi_array的[]操作除了接受整数来模拟原生数组以外,还接受boost::indices对象的[]操作所返回的东东(这个东东是index_gen类型,我们只管用,不予深究),向multi_array的[]操作传入这个东东可以得到一个子视图。
    • multi_array有个index_range类型,用于指定索引的起始、终止和步进值(默认是1)。boost::indices对象的[]操作接受这个类型的数据才能生成指定区域的子视图。
    • 子视图的用法和多维数组一样

    boost::indices对象的[]操作除了接受index_range,还可以接受整型数值,这样我们可以生成一个比原有数组的维度更少的子视图(也称为切片)。

    下面的代码取3维数组myarray的X轴为1的一个2维切片(假设把myarray看成一个3维空间,3个维度3x4x2分别对应于ZxYxX三个轴)

    1. ...
    2.   typedef array_type::index_range range;
    3.   array_type::array_view<2>::type myview =
    4.     myarray[ boost::indices[range(1,3)][range(0,4,2)][1] ];
    5.  
    6.   for (array_type::index i = 0; i != 2; ++i)
    7.     for (array_type::index j = 0; j != 2; ++j)
    8.         assert(myview[i][j] == myarray[i+1][j*2][1]);

  12. 存储的顺序

    每 个数组类都提供了接受一个存储顺序参数的构造函数。这在与某些跟标准C的数组存储顺序不同的遗留代码进行接口时非常有用,如 FORTRAN. 可选的值有 c_storage_order, fortran_storage_order, 和 general_storage_order.

    c_storage_order 是缺省值,它将以C数组相同的顺序在内存中存储各元素,即从后往前保存各个维度。

    fortran_storage_order 则以 FORTRAN 的顺序在内存中存储各个元素:从第一个维度到最后一个。注意,在使用这个参数时,数组的索引仍然保持为从零起计。

    例子

    1. typedef boost::multi_array<double,3> array_type;
    2. array_type A(boost::extents[3][4][2],boost::fortran_storage_order);
    3. call_fortran_function(A.data());

    general_storage_order 允许你定制在内存中保存各个维度的顺序,以及各个维度是按升序还是降序来保存。

    例子

    1. typedef boost::general_storage_order<3> storage;
    2. typedef boost::multi_array<int,3> array_type;
    3.  
    4. // 先保存最后一个维度,然后是第一个维度,最后是中间
    5. array_type::size_type ordering[] = {2,0,1};
    6.  
    7. // 以降序保存第一个维度(维度0)
    8. bool ascending[] = {false,true,true};
    9.  
    10. array_type A(extents[3][4][2],storage(ordering,ascending)); 

    改变数组的形状

    Boost.MultiArray 数组提供了整形操作。只要维数和元素数量保持不变,数组的形状即维度的大小改变。

    例子

    1. ...
    2.   boost::array<int,3> dims = {2, 3, 4};
    3.   myarray.reshape( dims );
    4.   //现在myarray是2x3x4的数组了

    改变数组的大小

    和 标准库的容器一样,boost::multi_array类也提供了保留元素的调整大小操作。维数必须保持一致,但每个维度的长度可以按需要增加或减少。 当一个数组被扩大时,原有元素将被复制到新的内存中,然后旧内存中的元素将被析构。而数组中的新元素将是缺省构造的。但是,如果新数组的某些维度的大小是 缩短的,则有些元素将不再可用。

    例子

    1. myarray.resize(boost::extents[1][2][3]);

  13. 使用multi_array做图像格式转换

    本站有AGGCImg的图像处理教程,AGG库倾向于矢量绘图,CImg倾向于图像处理。我们可以考虑双剑合壁,共同来生成我们要的图像。可是它们的内部数据格式却不完全相同:

    AGG的内部格式是 color buf[y][x][v]
    CImg的内部格式是 color buf[v][y][x]
    其中color为单通道颜色值、v代表颜色通道(如RGB三色)、x,y是坐标。我们这里让CImg的z轴为1,即二维图像。

    我们得找个方法可以方便地互相转换,这里我们选用multi_array来做这件事(另,GIL也是一个不错的候选方案,见本站GIL教程)。

    1. #include "boost/multi_array.hpp" // multi_array
    2. #include "cimg.h" // CImg
    3. #include <agg_pixfmt_rgb.h> //后面全是AGG的
    4. #include <agg_scanline_u.h>
    5. #include <agg_renderer_scanline.h>
    6. #include <../font_win32_tt/agg_font_win32_tt.h>
    7. #include <agg_font_cache_manager.h>
    8. #include <agg_conv_bspline.h>
    9. #include <agg_path_storage.h>
    10. #include <agg_conv_curve.h>
    11. #include <agg_conv_transform.h>
    12. #include <agg_ellipse.h>
    13. #include <agg_trans_single_path.h>
    14.  
    15. using cimg_library::CImg;
    16. int main () {
    17.     // AGG画图,写了一行字
    18.     char buf[200][300][3];
    19.     agg::rendering_buffer rbuf(
    20.             (unsigned char*)buf,
    21.             300, 200,
    22.             300*3);
    23.     agg::pixfmt_rgb24 pixf(rbuf);
    24.     agg::renderer_base<agg::pixfmt_rgb24> renb(pixf);
    25.  
    26.     typedef agg::font_engine_win32_tt_int16 fe_type;
    27.     typedef agg::font_cache_manager<fe_type> fcman_type;
    28.  
    29.     renb.clear(agg::rgba(0.5,0.5,1));
    30.     fe_type font(::GetDC(0));
    31.     fcman_type font_manager(font);
    32.     font.height(32.0);
    33.     font.flip_y(true);
    34.     font.hinting(true);
    35.     if(!font.create_font("Comic Sans MS",agg::glyph_ren_outline)) return -1;
    36.     //坐标转换管道
    37.     typedef agg::conv_curve<
    38.         fcman_type::path_adaptor_type
    39.     > cc_pa_type;
    40.     cc_pa_type ccpath(font_manager.path_adaptor());
    41.  
    42.     typedef agg::conv_transform<cc_pa_type,
    43.         agg::trans_single_path> ct_cc_pa_type;
    44.     agg::trans_single_path trans_path;
    45.     ct_cc_pa_type ctpath(ccpath, trans_path);
    46.     
    47.     agg::path_storage ps;
    48.     ps.move_to(20,100);
    49.     ps.line_rel(80,50);
    50.     ps.line_rel(100,-100);
    51.     ps.line_rel(100,100);
    52.     
    53.     agg::conv_bspline<agg::path_storage> cb_ps(ps);
    54.     trans_path.add_path(cb_ps);
    55.  
    56.     agg::rasterizer_scanline_aa<> ras;
    57.     agg::scanline_u8 sl;
    58.     
    59.     double x=0, y=0;
    60.     for(const wchar_t *p = L"http://www.cpp-prog.com"; *p; p++)
    61.     {
    62.         const agg::glyph_cache* gc = font_manager.glyph(*p);
    63.         if(gc)
    64.         {
    65.             font_manager.init_embedded_adaptors(gc, x, y);
    66.             ras.add_path(ctpath);
    67.  
    68.             x += gc->advance_x;
    69.             y += gc->advance_y;
    70.         }
    71.     }
    72.     agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba(1,0,0));
    73.  
    74.     // 定义CImg
    75.     CImg<unsigned char> img(300,200,1,3);
    76.  
    77.     // 用multi_array把AGG图像数据转成CImg
    78.     typedef boost::multi_array_ref<unsigned char,3> array_type;
    79.     // AGG->多维数组
    80.     /**
    81.     * AGG排列是[y][x][v], CImg是[v][y][x]
    82.     * 设y=2, x=1, v=0(C语言默认顺序)
    83.     * 则AGG的[2][1][0]对应CImg就是[0][2][1]
    84.     */
    85.     typedef boost::general_storage_order<3> storage;
    86.     array_type::size_type ordering[] = {0,2,1};
    87.     bool ascending[] = {true,true,true};
    88.     array_type array_agg((unsigned 

抱歉!评论已关闭.