最近开始使用Halide做点事情,说找点资料,结果网上基本都是翻译下官方教程,还得自己摸索。
Halide的目的是更方便地做优化,即不牺牲代码可读性的情况下利用硬件加速代码执行。更详细的说明参考原始论文。这里直接说怎么上手的事情。
Halide的文档确实不如OpenCV,这就是差距吧。就比如说,首先应当介绍下核心的数据结构以及围绕这些数据结构有哪些关键API,《Learning OpenCV》这方面就做得比较好。所以 这篇文章大致按照这个套路来,而不是上来就直奔有哪些优化操作。
看了Tutorial的第一个例子,感觉 它的Func的用法更像是函数式编程语言,或者说 像 Tensorflow的计算图,先定义好运算步骤,然后再进行实际运算。
我只需要对数组进行操作,并不一定是图像,所以先要把自己的数据转换成 Halide的Buffer,但Halide的前几个教程并没说怎么把自己内存中的数据转出它的Buffer类型,只有读图片的API,在网上找了会儿,后面又看到很靠后Tutorial里面 有 这样的写法:
Halide::Runtime::Buffer<uint8_t> planar_input(1024, 768, 3);
原来挺简单, Halide::Buffer也能这么创建,创建好以后访问就比较简单了
Halide::Buffer<float> input(10, 1, 3); // w * h * 3
for (int j = 0; j < input.height(); j++) {
for (int i = 0; i < input.width(); i++) {
for (int k = 0; k < 3; k++) {
input(i, j, k) = 1;
}
}
}
接下来是怎么写Func操作的问题, 主要是输出结果为多通道的情况。Halide的例子有个增强亮度,问题是它那里对每个通道的操作是一样的,但不是每个需求都这样。我需要把每个通道拿出来做特定运算,然后自己试图照猫画虎,直接写了如下代码,编译没问题,运行报错:
Halide::Func transformColod;
Halide::Var x, y;
Halide::Var c;
Halide::Expr xCoor = input(x, y, 0);
Halide::Expr yCoor = input(x, y, 1);
Halide::Expr zCoor = input(x, y, 2);
xCoor = Halide::cast<float>(xCoor);
yCoor = Halide::cast<float>(yCoor);
zCoor = Halide::cast<float>(zCoor);
std::cout << "aa\n";
std::cout << " input.channels() " << input.channels() << std::endl;
xCoor += 10;
yCoor += 20;
zCoor += 30;
transformColod(x, y, 0) = xCoor;
transformColod(x, y, 1) = yCoor;
transformColod(x, y, 2) = zCoor;
验证了下,如果不是3通道,transformColod(x, y) = xx, 也就是输出 只有一个通道是没问题的。然后在网上搜怎么写输出多通道的Func,居然没搜到,倒是看到官方文档说用tuple,但自己的需求还没这么复杂啊。
然后在 tutorial中看到 第13课 说了这方面的问题,原来很简单:
transformColod(x, y, c) = mux(c, { xCoor , yCoor, zCoor });
不过这样写要注意 调用bound和unroll,不然影响效率
这样写即可,运行没问题,全部代码如下
#include "Halide.h"
// We'll also include stdio for printf.
#include <stdio.h>
int mytest() {
// test code
Halide::Buffer<float> input(10, 1, 3); // w * h * 3
for (int j = 0; j < input.height(); j++) {
for (int i = 0; i < input.width(); i++) {
for (int k = 0; k < 3; k++) {
input(i, j, k) = 1;
}
}
}
for (int j = 0; j < input.height(); j++) {
for (int i = 0; i < input.width(); i++) {
for (int k = 0; k < 3; k++) {
printf(" %d ", input(i, j, k));
}
printf(" \n");
}
printf(" \n");
}
Halide::Func transformColod;
Halide::Var x, y;
Halide::Var c;
Halide::Expr xCoor = input(x, y, 0);
Halide::Expr yCoor = input(x, y, 1);
Halide::Expr zCoor = input(x, y, 2);
xCoor = Halide::cast<float>(xCoor);
yCoor = Halide::cast<float>(yCoor);
zCoor = Halide::cast<float>(zCoor);
std::cout << "aa\n";
std::cout << " input.channels() " << input.channels() << std::endl;
xCoor += 10;
yCoor += 20;
zCoor += 30;
// 2) It requires a select, which can impact performance if not
// bounded and unrolled:
// brighter.bound(c, 0, 3).unroll(c);
// 参考 lesson 13
// TODO: 这个操作要求 bound 和 循环展开!
// 2) It requires a select, which can impact performance if not
// bounded and unrolled:
// brighter.bound(c, 0, 3).unroll(c);
transformColod(x, y, c) = mux(c, { xCoor , yCoor, zCoor });
Halide::Buffer<float> output2 =
transformColod.realize(input.width(), input.height(), 3);
//transformColod.realize(output2);
std::cout << "bb\n";
for (int j = 0; j < output2.height(); j++) {
for (int i = 0; i < output2.width(); i++) {
for (int k = 0; k < output2.channels(); k++) {
printf(" %f ", output2(i, j, k));
}
printf(" \n");
}
printf(" \n");
}
return 0;
}
Visual Studio里面的配置 就配置下头文件目录、库文件目录,链接器选项里面加上lib文件即可。