在《玩转 ArrayFire:08 数组和矩阵操作》中,我们已经了解到 ArrayFire 的统一后端,在这一篇中,我们继续学习 ArrayFire 在 Forge 下的可视化操作。
ArrayFire 作为一个库,旨在为高性能、并行和 GPU 计算提供一个健壮且易于使用的平台。 Forge ——一个 OpenGL 可视化库——的目标也是提供同样健壮的可视化,并且能使 Arrayfire 数据结构和 OpenGL 互操作。
Arrayfire 提供了包装函数,它被设计成一个简单的接口来可视化 af::array。这些函数执行各种互操作任务。其中一个特别的例子是,我们可以直接从 GPU-data 到 GPU-framebuffer 中提取数据绘图,而不是浪费时间从 GPU 复制和重新格式化数据到主机再返回给 GPU !这节省了 2 份内存拷贝。
可视化可以用鼠标操作。可以采取以下措施:
- 缩放(Alt +鼠标左键点击,上下移动)
- 平移(左键点击并拖动)
- 旋转(鼠标右键-跟踪球旋转)
现在,让我们来看看:我们可以用 Forge 展示什么样的视觉效果,以及 Arrayfire 是如何在两个库之间处理数据的。
在调用 Forge 函数之前,我们需要建立相关的 “canvas” 类。Forge 函数被添加到 af::Window 类。首先让我们创建一个窗口:
const static int width = 512, height = 512;
af::Window window(width, height, "2D plot example title");
do{
//drawing functions here
} while( !window.close() );
ArrayFire 还添加了一个绘图循环,所以可以使用 Forge 的绘图函数来绘制窗口。
下面,介绍几种常见的绘图函数:
af::Window::image() 函数的作用是绘制灰度或彩色图像。要绘制灰度图像,应该将 2D 数组传递到函数中。让我们看一个静态噪声的例子:
array img = constant(0, width, height); //make a black image
array random = randu(width, height); //make random [0,1] distribution
img(random > 0.5) = 1; //set all pixels where distribution > 0.5 to white
window.image(img);
通过调整前面的例子,设置图像的RGB值为3,即产生彩色噪声:
array img = 255 * randu(width, height, 3); //make random [0, 255] distribution
window.image( img.as(u8) );
注意:Forge 会自动处理任何从 ArrayFire 传入的 af::array 类型。在第一个示例中,我们传入了一个范围为 [0,1] 的浮点数图像。在上一个示例中,我们将数组转换为范围为 [0,255] 的无符号字节数组。所有 Forge 绘图函数的类型处理属性是一致的。
af::Window::plot() 函数将数组可视化为二维线图。让我们看一个简单的例子:
array X = seq(-af::Pi, af::Pi, 0.01);
array Y = sin(X);
window.plot(X, Y);
plot 函数具有如下特征:
void plot( const array &X, const array &Y, const char * const title = NULL );
绘图时需要这些点的x坐标和y坐标。这些参数可以是非均匀的,或参数化的:
array t = seq(0, 100, 0.01);
array X = sin(t) * (exp(cos(t)) - 2 * cos(4 * t) - pow(sin(t / 12), 5));
array Y = cos(t) * (exp(cos(t)) - 2 * cos(4 * t) - pow(sin(t / 12), 5));
window.plot(X, Y);
af::Window::plot3() 函数具有如下特征:
void plot3 (const array &in, const char * title = NULL);
输入数组需要按顺序输入 XYZ 坐标。这些点可以是一维(3n x 1)数组,也可以是 (3 x n) 或 (n x 3) 的矩阵。
array Z = seq(0.1f, 10.f, 0.01);
array Y = sin(10 * Z) / Z;
array X = cos(10 * Z) / Z;
array Pts = join(1, X, Y, Z);
//Pts can be passed in as a matrix in the from n x 3, 3 x n
//or in the flattened xyz-triplet array with size 3n x 1
window.plot3(Pts);
//both of the following are equally valid
//window.plot3(transpose(Pts));
//window.plot3(flat(Pts));
af::Window::hist() 函数的作用是将输入数组渲染成直方图。在下面的示例中,输入数组将使用 Arrayfire 的 histogram() 函数创建,该函数计算并存储每个样本。histogram() 的输出可以直接输入到 af::Window::hist() 函数。
const int BINS = 128; SAMPLES = 9162;
array norm = randn(SAMPLES);
array hist_arr = histogram(norm, BINS);
win.hist(hist_arr, 0, BINS);
除了直方图数组外,af::Window::hist() 函数还接受两个其他参数:直方图数组中所有数据点的最小值和最大值。这有效地设置了存储数据的范围。
void hist(const array & X, const double minval, const double maxval, const char * const title = NULL);
af::Window::surface() 函数将数组绘制成一个 3D 曲面。
array Z = randu(21, 21);
window.surface(Z, "Random Surface"); //equal to next function call
//window.surface( seq(-1, 1, 0.1), seq(-1, 1, 0.1), Z, "Random Surface");
af::Window::surface() 函数有两个重载:
void surface (const array & S, const char * const title ) // Accepts a 2d matrix with the z values of the surface
void surface (const array &xVals, const array &yVals, const array &S, const char * const title) // accepts additional vectors that define the x,y coordinates for the surface points.
第二个重载对于 X、Y 坐标向量有两个选项。假设一个大小为 m x n 的表面网格:
ArrayFire 中有相当全面的方法来可视化数据。由于使用高性能 GPU 绘图库 Forge ,所提供的 Arrayfire 函数不仅使可视化尽可能简单,而且保持了 Arrayfire 库的其余部分的健壮性。